代码之家  ›  专栏  ›  技术社区  ›  RPM1984

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

  •  6
  • RPM1984  · 技术社区  · 14 年前

    我用的是“ 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. 当前我的 单位工程 接口只有一个方法: void Commit();
    4. 控制器有 IUserContentService IUnitOfWork 由DI注入
    5. IUserContentService服务 电话 Find 在存储库中,使用 ObjectContext .

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

    1. 我不想把 单位工程 作为 MySqlServerObjectContext .
    2. 我不想让控制器去关心 ApplyCurrentValues

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

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

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

    我已经有了根据类型(泛型的组合,复数)计算实体集名称的智能,所以不要太担心这个问题。

    但是 我在想最好的地方放在哪里 应用当前值 ? 把它放在 单位工程 接口,因为这是一个持久性(EF)问题。因为同样的原因它不属于服务。如果我把它放在我的 MySqlServerObjectContext 类(有意义),我从哪里调用它,因为没有任何东西可以直接访问这个类-当有东西请求时,它通过DI注入 单位工程 .

    有什么想法吗?

    编辑

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

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

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

    有什么EF4专家可以帮忙吗?

    编辑

    无所谓-找到了解决方案,请参阅下面的答案。

    1 回复  |  直到 7 年前
        1
  •  8
  •   Community pid    7 年前

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

    控制器相关代码:

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

    所以,我的控制器调用一个名为 Update IUserContentService ,通过强类型 Review 反对。

    用户内容服务相关代码

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

    所以,我的服务调用一个名为 UpdateModel IPostRepository ,通过强类型 回顾 反对。

    现在,这里是棘手的部分。

    我真的有 没有特定的存储库 . 我有一个 通用存储库 打电话 GenericRepository<T> : IRepository<T> ,它处理 所有不同的仓库。

    所以当有东西要求 IPostRepository公司 (这是我的服务),我会给它一个 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<T> 不会知道的。

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

    编辑

    这种“存根技术”也适用于:

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

    但问题是因为 岗位 是抽象的,我不能实例化,因此必须检查Post的类型并为 每一个 派生类型。不是一个真正的选择。

    编辑2(上次)

    好吧,使用“存根技术”处理抽象类,现在并发问题解决了。

    我向 更新模型 方法和特殊 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的类型,防止并发问题,也防止了对DB的额外访问。

    非常棒。

    编辑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);
    

    但它总是回来 无效的 ,即使在探索OSM时,我也可以看到我的实体在那里使用相同的键。

    但这条规则有效:

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

    也许因为我使用的是纯POCO,OSM很难找出实体密钥,谁知道呢。

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

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

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

    是的-它使用反射,但它是聪明的反射(基于属性),我已经在使用反射对T中的实体集名称进行多极化。

    不管怎样,问题解决了-现在一切正常!