6

我正在使用“存根技术”来更新我的 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接口只有 1 种方法:void Commit();
  4. 控制器有IUserContentServiceDIIUnitOfWork注入
  5. IUserContentService调用Find存储库,它使用ObjectContext.

这是我不喜欢上面的代码的两件事:

  1. 我不想将IUnitOfWork 转换MySqlServerObjectContext.
  2. 我不希望控制器必须关心ApplyCurrentValues

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

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

有什么想法我该怎么做?(或类似的东西)。

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

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

有什么想法吗?

编辑

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

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

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

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

编辑

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

4

1 回答 1

8

想通了 - 并不容易,所以我会尽力解释。(给关心的人)

控制器相关代码:

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

因此,我的控制器调用了一个名为Updateon的方法IUserContentService,通过强类型Review对象传递。

用户内容服务相关代码

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

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

现在,这是棘手的部分。

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

所以当有东西请求 a 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() 约束

执行:

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(好的 - 就是这样!)

由于这个 SO 答案,我找到了解决方案:Is is possible to check if an object is already attach 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 的实体集名称进行多元化。

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

于 2010-11-19T02:31:38.627 回答