实体框架5工作单元模式 - 我应该在哪里调用SaveChanges?

时间:2012-12-09 12:14:45

标签: entity-framework asp.net-mvc-4 repository-pattern unit-of-work savechanges

如果这似乎是一个重复的问题,请提前道歉。 This question是我能找到的最接近的,但它并没有真正解决我所面临的问题。

我在ASP.NET MVC4应用程序中使用Entity Framework 5并尝试实现工作单元模式。

我的工作单元类实现IDisposable并包含我的DbContext派生对象上下文类的单个实例,以及许多存储库,每个存储库都派生自通用基础存储库类这暴露了所有常见的存储库功能。

对于每个HTTP请求,Ninject创建一个Unit of Work类的单个实例并将其注入控制器,并在请求完成时自动处理它。

由于EF5抽象出数据存储,Ninject管理对象上下文的生命周期,这似乎是消费代码访问内存中实体对象而不需要明确管理其持久性的完美方式。换句话说,为了最佳地分离关注点,我设想我的控制器操作方法能够使用和修改存储库数据,而无需事后显式调用SaveChanges

我实现这个想法的第一次(naiive)尝试在每个修改数据的存储库基类方法中调用了SaveChanges。当然,我很快就意识到这既没有进行性能优化(特别是在对同一个方法进行多次连续调用时),也没有适应动作方法直接修改从存储库中检索的对象属性的情况。

所以,我进化了我的设计以消除对SaveChanges的这些过早调用,并在处理工作单元实例时用一个调用替换它们。这似乎是MVC中工作单元模式最干净的实现,因为工作单元自然是作用于请求。

不幸的是,在构建了这个概念后,我发现了它的致命缺陷 - 在DbContext中添加或删除的对象是not reflected, even locally,直到SaveChanges被调用为止。

那么,您对消费代码应该能够使用对象而不明确地持久化它的想法有何看法?而且,如果这个想法看起来有效,那么用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());

1 个答案:

答案 0 :(得分:7)

所有SaveChanges

首先应该根本不会存在于存储库中。因为这会导致你失去UnitOfWork的好处。

第二次您需要制作一种特殊方法来保存UnitOfWork中的更改。

如果您想自动调用此方法,那么您可以使用其他解决方案,例如ActionFilter,或者让所有控制器继承自BaseController类并处理其中的SaveChanges

无论如何,UnitOfWork应始终采用SaveChanges方法。