如果这看起来像是一个重复的问题,请提前道歉。这个问题是我能找到的最接近的问题,但它并没有真正解决我面临的问题。
我正在 ASP.NET MVC4应用程序中使用实体框架5,并尝试实现工作单元模式。
我的工作单元类实现了IDisposable
,并包含我的DbContext
派生对象上下文类的单个实例,以及许多存储库,每个存储库都派生自公开所有常用存储库功能的通用基本存储库类。
对于每个 HTTP 请求,Ninject 都会创建工作单元类的单个实例并将其注入控制器,并在请求完成时自动释放它。
由于 EF5 抽象化了数据存储,而 Ninject 管理对象上下文的生存期,因此这似乎是使用代码访问内存中实体对象而无需显式管理其持久性的完美方式。换句话说,为了最佳地分离关注点,我设想我的控制器操作方法能够使用和修改存储库数据,而无需事后显式调用SaveChanges
。
我实现这个想法的第一次(天真)尝试是在修改数据的每个存储库基类方法中调用SaveChanges
。当然,我很快意识到这既不是性能优化的(尤其是在对同一方法进行多次连续调用时),也不能适应操作方法直接修改从存储库检索的对象属性的情况。
因此,我改进了我的设计,以消除这些过早的SaveChanges
调用,并在释放工作单元实例时将它们替换为单个调用。这似乎是 MVC 中工作单元模式最干净的实现,因为工作单元自然作用域为请求。
不幸的是,在构建这个概念之后,我发现了它的致命缺陷 - 在调用SaveChanges
之前,添加到DbContext
或删除的对象不会反映,即使是在本地。
那么,对于使用代码应该能够使用对象而不显式持久化它们的想法,您有什么看法?而且,如果这个想法看起来有效,那么使用 EF5 实现它的最佳方法是什么?
非常感谢您的建议,
提姆
更新:根据@Wahid的响应,我在下面添加了一些测试代码,这些代码显示了消费代码必须显式调用SaveChanges
的一些情况:
var unitOfWork = _kernel.Get<IUnitOfWork>();
var terms = unitOfWork.Terms.Entities;
// Purge the table so as to start with a known state
foreach (var term in terms)
{
terms.Remove(term);
}
unitOfWork.SaveChanges();
Assert.AreEqual(0, terms.Count());
// Verify that additions are not even reflected locally until committed.
var created = new Term { Pattern = "Test" };
terms.Add(created);
Assert.AreEqual(0, terms.Count());
// Verify that additions are reflected locally once committed.
unitOfWork.SaveChanges();
Assert.AreEqual(1, terms.Count());
// Verify that property modifications to entities are reflected locally immediately
created.Pattern = "Test2";
var another = terms.Single(term => term.Id == created.Id);
Assert.AreEqual("Test2", another.Pattern);
Assert.True(ReferenceEquals(created, another));
// Verify that queries against property changes fail until committed
Assert.IsNull(terms.FirstOrDefault(term => term.Pattern == "Test2"));
// Verify that queries against property changes work once committed
unitOfWork.SaveChanges();
Assert.NotNull(terms.FirstOrDefault(term => term.Pattern == "Test2"));
// Verify that deletions are not even reflected locally until committed.
terms.Remove(created);
Assert.AreEqual(1, terms.Count());
// Verify that additions are reflected locally once committed.
unitOfWork.SaveChanges();
Assert.AreEqual(0, terms.Count());
首先,SaveChanges
根本不应该出现在存储库中。因为这会导致您失去UnitOfWork
的好处。
其次,您需要使用特殊方法来保存UnitOfWork
中的更改。
如果你想自动调用这个方法,那么你可能会罚款一些其他的解决方案,比如ActionFilter
,或者让你所有的控制器都继承BaseController
类并处理其中的SaveChanges
。
无论如何,UnitOfWork
应该始终具有SaveChanges
方法。