实体框架4 - 在哪里放置“ApplyCurrentValues”逻辑?

时间:2010-11-19 01:12:43

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

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

这是我目前在我的控制器中有的代码(有效):

[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. 我基本上都使用依赖注入(基于接口)
  2. 我使用工作单元模式来抽象ObjectContext并提供跨多个存储库的持久性
  3. 目前我的 IUnitOfWork 界面只有一种方法:void Commit();
  4. 控制器由DI
  5. 注入IUserContentServiceIUnitOfWork
  6. IUserContentService在存储库中调用Find,使用ObjectContext
  7. 以上代码中我不喜欢这两件事:

    1. 我不想将 IUnitOfWork 强制转换为MySqlServerObjectContext
    2. 我不希望Controller必须关心ApplyCurrentValues
    3. 我基本上希望我的代码看起来像这样:

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

      我有什么想法可以做到这一点? (或类似的东西)。

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

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

      有什么想法吗?

      修改

      我在下面使用存根技术有一个解决方案,但问题是如果我已经检索了我事先更新的实体,它会抛出异常,说明具有该密钥的实体已经存在。

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

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

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

      修改

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

1 个答案:

答案 0 :(得分:8)

想出来 - 并不容易,所以我会尽力解释。 (对于那些关心的人)

控制器相关代码:

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

因此,我的控制器在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>不知道的特定实体密钥。

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

修改

这种“存根技术”也有效:

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

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

编辑2(上次)

好的,让“存根技术”使用抽象类,所以现在解决了并发问题。

我在 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);
}

<强>接口

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

这可以防止我必须手动找出T的类型,防止出现并发问题,并防止额外的数据库访问。

非常时髦。

编辑3(我认为最后一次是最后一次)

上面的“存根技术”有效,但如果我事先检索到该对象,它会抛出一个异常,说明OSM中已存在具有该密钥的实体。

有人可以建议如何处理这个问题吗?

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

我找到了解决方案,多亏了这个答案: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的时候,我可以看到我的实体在那里使用相同的密钥。

但是这段代码有效:

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

也许是因为我使用Pure POCO,OSM无法搞清楚实体密钥,谁知道。

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

所有POCO必须有1个用该属性修饰的公共属性,或者我在我的存储库模块中抛出异常。

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

是的 - 它使用反射,但它是聪明的反射(基于属性),我已经使用反射来实现T中实体集名称的叠加。

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