1

我有一个存储库,例如 UserRepository。它通过给定的 userId 返回一个用户。我在 Web 应用程序上工作,因此对象在请求结束时被加载到内存中、使用和处置。

到目前为止,当我编写存储库时,我只是从数据库中检索数据。我不会将检索到的 User 对象存储到内存中(我的意思是存储库的集合中)。当调用存储库的 GetById() 方法时,我不检查对象是否已经在集合中。我只是查询数据库。

我的问题是

  1. 我是否应该将检索到的对象存储在内存中,并且在调用存储库的 Get 方法时,是否应该在进行任何数据库调用之前先检查该对象是否存在于内存中?
  2. 或者内存收集是不必要的,因为 Web 请求是一个短暂的会话,并且所有对象都在之后被释放
4

3 回答 3

3

1)我是否应该将检索到的对象存储在内存中,并且在调用存储库的 Get 方法时,是否应该在进行任何数据库调用之前先检查该对象是否存在于内存中?

由于您的存储库应该足够抽象以模拟内存中集合的目的,我认为这真的取决于您和您的用例。

如果您在从数据库中检索对象后存储您的对象,您可能最终会得到一个所谓的IdentityMap. 如果你这样做,它会变得非常复杂(这取决于你的域)。

根据您所依赖的基础设施层,您可以使用 ORM 提供的 IdentityMap(如果有)。

但真正的问题是,是否值得实施 IdentityMap?

我的意思是,我们同意重复查询可能是错误的,原因有两个,性能和完整性,这里引用 Martin Fowler 的话:

一句古老的谚语说,一个有两只手表的人永远不知道现在几点了。如果两个手表令人困惑,那么从数据库加载对象可能会变得更加混乱。

但有时你需要务实,每次需要时都加载它们。

2)或者内存收集是不必要的,因为网络请求是一个短暂的会话,所有对象都在之后被释放

It depends™,例如,在某些情况下,您可能必须在不同的地方使用您的对象,在这种情况下,这可能是值得的,但是假设您需要通过从数据库加载您的用户来刷新您的用户会话身份,然后在某些情况下,您只在整个请求中执行一次。

于 2013-07-19T09:01:54.237 回答
1

当您使用像 Entity Framework 或 NHibernate 这样的 ORM 时,它已经被处理了 - 所有读取的实体都通过 IdentityMap 机制进行跟踪,如果实体已经加载,则通过键搜索(EF 中的 DbSet.Find)甚至不会命中数据库.

如果您使用直接数据库访问或 microORM 作为存储库的基础,则应该小心 - 如果没有 IdentityMap,您实际上是在使用值对象:

using System;
using System.Collections.Generic;
using System.Linq;

namespace test
{
internal class Program
{
    static void Main()
    {
        Console.WriteLine("Identity map");
        var artrepo1 = new ArticleIMRepository();
        var o1 = new Order();
        o1.OrderLines.Add(new OrderLine {Article = artrepo1.GetById(1, "a1", 100), Quantity = 50});
        o1.OrderLines.Add(new OrderLine {Article = artrepo1.GetById(1, "a1", 100), Quantity = 30});
        o1.OrderLines.Add(new OrderLine {Article = artrepo1.GetById(2, "a2", 100), Quantity = 20});
        o1.ConfirmOrder();
        o1.PrintChangedStock();
        /*
        Art. 1/a1, Stock: 20
        Art. 2/a2, Stock: 80
        */

        Console.WriteLine("Value objects");
        var artrepo2 = new ArticleVORepository();
        var o2 = new Order();
        o2.OrderLines.Add(new OrderLine {Article = artrepo2.GetById(1, "a1", 100), Quantity = 50});
        o2.OrderLines.Add(new OrderLine {Article = artrepo2.GetById(1, "a1", 100), Quantity = 30});
        o2.OrderLines.Add(new OrderLine {Article = artrepo2.GetById(2, "a2", 100), Quantity = 20});
        o2.ConfirmOrder();
        o2.PrintChangedStock();
        /*
        Art. 1/a1, Stock: 50
        Art. 1/a1, Stock: 70
        Art. 2/a2, Stock: 80
        */
        Console.ReadLine();
    }
    #region "Domain Model"
    public class Order
    {
        public List<OrderLine> OrderLines = new List<OrderLine>();

        public void ConfirmOrder()
        {
            foreach (OrderLine line in OrderLines)
            {
                line.Article.Stock -= line.Quantity;
            }
        }

        public void PrintChangedStock()
        {
            foreach (var a in OrderLines.Select(x => x.Article).Distinct())
            {
                Console.WriteLine("Art. {0}/{1}, Stock: {2}", a.Id, a.Name, a.Stock);
            }
        }
    }

    public class OrderLine
    {
        public Article Article;
        public int Quantity;
    }

    public class Article
    {
        public int Id;
        public string Name;
        public int Stock;
    }
    #endregion

    #region Repositories
    public class ArticleIMRepository
    {
        private static readonly Dictionary<int, Article> Articles = new Dictionary<int, Article>();

        public Article GetById(int id, string name, int stock)
        {
            if (!Articles.ContainsKey(id))
                Articles.Add(id, new Article {Id = id, Name = name, Stock = stock});
            return Articles[id];
        }
    }

    public class ArticleVORepository
    {
        public Article GetById(int id, string name, int stock)
        {
            return new Article {Id = id, Name = name, Stock = stock};
        }
    }
    #endregion
}
}
于 2013-07-19T19:52:55.350 回答
1

与通常情况一样,我认为不会出现“一刀切”的情况。

在某些情况下,当经常检索数据时,可能会在存储库上实施某种形式的缓存,不会过快过时,或者只是为了提高效率。

但是,您可以很好地实现一种通用缓存装饰器,它可以在您确实需要时包装存储库。

因此,应该根据优点来对待每个用例。

于 2013-07-19T12:15:00.347 回答