c# - Entity Framework 4 - 在哪里放置 "ApplyCurrentValues"逻辑?

标签 c# design-patterns entity-framework-4 unit-of-work

我正在使用“ stub technique ”来更新我的 POCO(在分离上下文中使用,ASP.NET MVC)。

这是我目前在我的 Controller 中的代码(有效):

[HttpPost]
public ActionResult Edit(Review review)
{
   Review originalReview = _userContentService.FindById(review.PostId) as Review;
   var ctx = _unitOfWork as MySqlServerObjectContext;
   ctx.ApplyCurrentValues("MyEntities.Posts", review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

如你所见,到处都是代码的味道。 :)

几点:

  1. 我基本上对所有事情都使用依赖注入(inject)(基于接口(interface))
  2. 我使用工作单元模式来抽象 ObjectContext 并提供跨多个存储库的持久性
  3. 目前我的IUnitOfWork 接口(interface)只有一种方法:void Commit();
  4. Controller 有 IUserContentServiceIUnitOfWork通过 DI 注入(inject)
  5. IUserContentService电话 Find在存储库中,它使用 ObjectContext .

以上代码有两点我不喜欢:

  1. 我不想将 IUnitOfWork 转换为 MySqlServerObjectContext .
  2. 我不希望 Controller 必须关心 ApplyCurrentValues

我基本上希望我的代码看起来像这样:

[HttpPost]
public ActionResult Edit(Review review)
{
   _userContentService.Update(review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

有什么办法可以做到吗? (或类似的东西)。

我已经很聪明地根据类型(泛型的组合,复数)计算出实体集名称,所以不要太担心。

但是我想知道最好放在哪里ApplyCurrentValues?放在IUnitOfWork里似乎不太合适接口(interface),因为这是一个持久性 (EF) 问题。出于同样的原因,它不属于服务。如果我把它放在我的 MySqlServerObjectContext类(有意义),我会从哪里调用它,因为没有任何东西可以直接访问这个类——它是在请求时通过 DI 注入(inject)的 IUnitOfWork .

有什么想法吗?

编辑

我有一个使用 stub 技术的解决方案,但问题是如果我已经检索到我预先更新的实体,它会抛出异常,说明具有该键的实体已经存在。

这是有道理的,虽然我不确定如何解决这个问题?

我是否需要“检查实体是否已经附加,如果没有,附加它?”

任何 EF4 专家都可以提供帮助吗?

编辑

没关系 - 找到解决方案,请参阅下面的答案。

最佳答案

弄清楚了 - 并不容易,所以我会尽力解释。 (对于关心的人)

Controller 相关代码:

// _userContentService is IUserContentService
_userContentService.Update(review);

因此,我的 Controller 调用了一个名为 Update 的方法在 IUserContentService ,通过强类型 Review对象。

用户内容服务相关代码

public void Update(Post post)
{
   // _userContentRepository is IPostRepository
   _userContentRepository.UpdateModel(post);
}

因此,我的服务调用了一个名为 UpdateModel 的方法在 IPostRepository ,通过强类型 Review对象。

现在,这是棘手的部分。

我实际上没有特定的存储库。我有一个名为 GenericRepository<T> : IRepository<T>通用存储库 ,它处理所有不同的存储库。

所以当某些东西请求 IPostRepository 时(我的服务正在做的),DI 会给它一个 GenericRepository<Post> .

但是现在,我给它一个PostRepository :

public class PostRepository : GenericRepository<Post>, IPostRepository
{
   public void UpdateModel(Post post)
   {
      var originalPost = CurrentEntitySet.SingleOrDefault(p => p.PostId == post.PostId);
      Context.ApplyCurrentValues(GetEntityName<Post>(), post);
   }
}

并且因为该类派生GenericRepository,它继承了所有核心存储库逻辑(查找、添加等)。

起初,我试图将 UpdateModel 代码放在 GenericRepository 类本身中(然后我就不需要这个特定的存储库了),但问题是检索现有实体的逻辑基于特定实体键,GenericRepository<T>不知道。

但最终结果是拼接隐藏在数据层的深处,我最终得到了一个非常干净的 Controller 。

编辑

这种“ stub 技术”也有效:

public void UpdateModel(Post post)
{
   var stub = new Review {PostId = post.PostId};
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}

但问题是因为 Post 是抽象的,我无法实例化,因此必须检查 Post 的类型并为每个派生类型创建 stub 。不是真正的选择。

编辑 2(最后一次)

好的,得到了​​使用抽象类的“ stub 技术”,那么现在并发问题就解决了。

我在我的 UpdateModel 方法中添加了一个通用类型参数,以及特殊的 new() constraint .

实现:

public void UpdateModel<T>(T post) where T : Post, new()
{
   var stub = new T { PostId = post.PostId };
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>, post);
}

接口(interface):

void UpdateModel<T>(T post) where T : Post, new();

这使我不必手动找出 T 的类型,防止并发问题,也防止额外访问数据库。

非常时髦。

EDIT 3(我以为最后一次是最后一次)

上面的“ stub 技术”有效,但如果我事先检索对象,它会抛出一个异常,指出具有该键的实体已经存在于 OSM 中。

谁能建议如何处理这个问题?

编辑 4(好的 - 就是这样!)

我找到了解决方案,多亏了这个 SO 答案:Is is possible to check if an object is already attached to a data context in Entity Framework?

我曾尝试使用以下代码“检查实体是否已附加”:

ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);

但它总是返回 null,即使在我探索 OSM 时我也可以看到我的实体在那里有相同的 key 。

但是这段代码有效:

CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)

也许因为我使用的是 Pure POCO,OSM 无法确定实体 key ,谁知道呢。

哦,我还添加了另一件事 - 为了不必为每个实体添加特定的存储库,我创建了一个名为“[EntityKey]”的属性(公共(public)属性属性)。

所有 POCO 都必须有 1 个用该属性修饰的公共(public)属性,否则我会在我的存储库模块中抛出异常。

然后我的通用存储库会查找此属性以创建/设置 stub 。

是的——它使用了反射,但它是一种聪明的反射(基于属性),我已经在使用反射对来自 T 的实体集名称进行复数化。

无论如何,问题已解决 - 现在一切正常!

关于c# - Entity Framework 4 - 在哪里放置 "ApplyCurrentValues"逻辑?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4221275/

相关文章:

c# - 使用 MVVM 设计模式的应用程序中的 Controller 是什么

sql - 我应该在什么情况下使用 Entity SQL?

c# - 使用 MVC3 保存对复杂模型的更新

c# - 如何创建具有 EntityState 未知关系的对象

c# - xpath 查询在网页中搜索字符串

c# - 如何在 Netsuite SuiteTalk 中运行搜索类型 "saved search"的已保存搜索

c# - 在 C# 中引用嵌套类对象

vb.net - 我正在开始一个新的 VB 项目,我可以使用一些指导

java - 提取特定行java

c# - 如何使用 Pcap.NET 和 C# 识别 LLC、RAW 和 SNAP?