简体   繁体   English

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

[英]Entity Framework 4 - Where to put “ApplyCurrentValues” Logic?

I'm using the " stub technique " to update my POCO's (used in a detached context, ASP.NET MVC). 我正在使用“ 存根技术 ”来更新我的POCO(用于分离的上下文,ASP.NET MVC)。

This is the code i currently have in my controller (which works): 这是我目前在我的控制器中的代码(可以工作):

[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..
}

As you can see, there is code smell everywhere. 如你所见,到处都有代码味道。 :) :)

A few points: 几点:

  1. I use Dependency Injection (interface-based) for basically everything 我基本上都使用依赖注入(基于接口)
  2. I use the Unit of Work pattern to abstract ObjectContext and provide persistence across multiple repositories 我使用工作单元模式来抽象ObjectContext并在多个存储库之间提供持久性
  3. Currently my IUnitOfWork interface has only 1 method: void Commit(); 目前我的IUnitOfWork接口只有一个方法: void Commit();
  4. Controller have IUserContentService and IUnitOfWork inject by DI 控制器通过DI注入IUserContentServiceIUnitOfWork
  5. IUserContentService calls Find in Repositories, which use the ObjectContext . IUserContentService在存储库中调用Find ,它使用ObjectContext

These are two things i don't like with my above code: 这些是我不喜欢上面代码的两件事:

  1. I don't want to cast the IUnitOfWork as MySqlServerObjectContext . 我不想将IUnitOfWork转换MySqlServerObjectContext
  2. I don't want the Controller to have to care about ApplyCurrentValues 我不希望Controller必须关心ApplyCurrentValues

I basically want my code to look like this: 我基本上希望我的代码看起来像这样:

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

Any ideas how i can do that? 任何想法我怎么能这样做? (or something similar). (或类似的东西)。

I already have smarts to work out the entity set name based on the type (combination of generics, pluralization), so don't worry too much about that. 我已经很聪明地根据类型(泛型,复数的组合)来计算实体集名称,所以不要过于担心。

But i'm wondering where the best place to put ApplyCurrentValues is ? 我想知道放置ApplyCurrentValues的最佳位置在ApplyCurrentValues It doesn't seem appropriate to put it in the IUnitOfWork interface, as this is a persistence (EF) concern. 将它放在IUnitOfWork接口中似乎不合适,因为这是一个持久性(EF)问题。 For the same reason it doesn't belong in the Service. 出于同样的原因,它不属于服务。 If i put it in my MySqlServerObjectContext class (makes sense), where would i call this from, as nothing directly has access to this class - it is injected via DI when something requests IUnitOfWork . 如果我把它放在我的MySqlServerObjectContext类中(有意义),我会在哪里调用它,因为没有任何东西可以直接访问这个类 - 当有东西请求IUnitOfWork时它会通过DI注入。

Any thoughts? 有什么想法吗?

EDIT 编辑

I have a solution below using the stub technique, but the problem is if i had retrieved the entity i am updating beforehand, it throws an exception, stating an entity with that key already exists. 我在下面使用存根技术有一个解决方案,但问题是如果我已经检索了我正在更新的实体,它会抛出异常,说明具有该密钥的实体已经存在。

Which makes sense, although i'm not sure how can resolve this? 这是有道理的,虽然我不知道如何解决这个问题?

Do i need to "check if the entity is already attached, if not, attach it?" 我是否需要“检查实体是否已经附加,如果没有,请附上它?”

Can any EF4 experts out there help? 任何EF4专家都可以提供帮助吗?

EDIT 编辑

Nevermind - found the solution, see answer below. 没关系 - 找到了解决方案,请参阅下面的答案。

Figured it out - wasn't easy, so i'll try to explain best i can. 想出来 - 不容易,所以我会尽力解释。 (for those who care) (对于那些关心的人)

Controller Relevant Code: 控制器相关代码:

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

So, my controller calls a method called Update on IUserContentService , passing through the strongly-typed Review object. 因此,我的控制器在IUserContentService上调用一个名为Update的方法,并通过强类型的Review对象。

User Content Service Relevant Code 用户内容服务相关代码

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

So, my service calls a method called UpdateModel on IPostRepository , passing through the strongly-typed Review object. 因此,我的服务在IPostRepository上调用一个名为UpdateModel的方法,并通过强类型的Review对象。

Now, here is the tricky part. 现在,这是棘手的部分。

I actually have no specific Repositories . 我实际上没有特定的存储库 I have a generic repository called GenericRepository<T> : IRepository<T> , which handles all the different repositories. 我有一个名为GenericRepository<T> : IRepository<T>通用存储库 ,它处理所有不同的存储库。

So when something requests a IPostRepository (which my service was doing), DI would give it a GenericRepository<Post> . 因此当某些东西请求IPostRepository (我的服务正在进行)时,DI会给它一个GenericRepository<Post>

But now, i give it a PostRepository : 但现在,我给它一个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);
   }
}

And because the class derives from GenericRepository , it inherits all the core repository logic (Find, Add, etc). 并且因为该类派生GenericRepository ,它继承了所有核心存储库逻辑(Find,Add等)。

At first, i tried to put that UpdateModel code in the GenericRepository class itself (and then i wouldn't have needed this specific repository), but the problem is the logic to retrieve the existing entity is based on a specific entity key, which the GenericRepository<T> would not know about. 起初,我试图将UpdateModel代码放在GenericRepository类本身中(然后我就不需要这个特定的存储库),但问题是检索现有实体的逻辑是基于特定的实体密钥, GenericRepository<T>不会知道。

But the end result is the stitching is hidden deep down in the depths of the data layer, and i end up with a really clean Controller. 但最终的结果是缝合深藏在数据层的深处,我最终得到了一个非常干净的控制器。

EDIT 编辑

This "stub technique" also works: 这种“存根技术”也有效:

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

But the problem is because Post is abstract, i cannot instantiate and therefore would have to check the type of Post and create stubs for every single derived type. 但问题是因为Post是抽象的,我无法实例化,因此必须检查Post的类型并为每个派生类型创建存根。 Not really an option. 不是一个选择。

EDIT 2 (LAST TIME) 编辑2(上一次)

Okay, got the "stub technique" working with abstract classes, so now the concurrency issue is solved. 好吧,让“存根技术”与抽象类一起工作,所以现在解决了并发问题。

I added a generic type parameter to my UpdateModel method, and the special new() constraint . 我在UpdateModel方法中添加了一个泛型类型参数,以及特殊的new()约束

Implementation: 执行:

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();

This prevents me from having to figure out the type of T manually, prevents concurrency issues and also prevents an extra trip to the DB. 这可以防止我不得不手动找出T的类型,防止并发问题,并防止额外的数据库访问。

Pretty groovy. 非常时髦。

EDIT 3 (i thought the last time was the last time) 编辑3(我认为最后一次是最后一次)

The above "stub technique" works, but if i retrieve the object beforehand, it throws an exception stating an entity with that key already exists in the OSM. 上面的“存根技术”可以工作,但是如果我事先检索对象,它会抛出一个异常,说明OSM中已经存在具有该密钥的实体。

Can anyone advise how to handle this? 任何人都可以建议如何处理这个?

EDIT 4 (OK - this is it!) 编辑4(好的 - 这就是它!)

I found the solution, thanks to this SO answer: Is is possible to check if an object is already attached to a data context in Entity Framework? 我找到了解决方案,多亏了这个答案: 是否可以检查对象是否已经附加到Entity Framework中的数据上下文?

I had tried to "check if the entity is attached" using the following code: 我曾尝试使用以下代码“检查实体是否已附加”:

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

But it always returned null , even through when i explored the OSM i could see my entity there with the same key. 但它总是返回null ,即使在我探索OSM的时候,我可以看到我的实体在那里使用相同的密钥。

But this code works: 但是这段代码有效:

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

Maybe because i'm using Pure POCO's, the OSM had trouble figuring out the entity key, who knows. 也许是因为我使用Pure POCO,OSM很难搞清楚实体密钥,谁知道呢。

Oh and one other thing i added - so that i don't have to add a specific repository for each entity, i created an attribute called " [EntityKey] " (public property attribute). 哦,我添加的另一件事 - 所以我不必为每个实体添加一个特定的存储库,我创建了一个名为“ [EntityKey] ”的属性(公共属性属性)。

All POCO's must have 1 public property decorated with that attribute, or i throw an exception in my repository module. 所有POCO必须有1个用该属性修饰的公共属性,或者我在我的存储库模块中抛出异常。

So my generic repository then looks for this property in order to create/setup the stub. 因此,我的通用存储库然后查找此属性以创建/设置存根。

Yes - it uses reflection, but it's clever reflection (attribute-based) and i'm already using reflection for plularization of entity set names from T. 是的 - 它使用反射,但它是巧妙的反射(基于属性),我已经使用反射来实现T的实体集名称的多层化。

Anyway, problem solved - all working fine now! 无论如何,问题解决了 - 现在一切正常!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM