实体框架4-在哪里放置“ applyCurrentValues”逻辑?
-
26-09-2019 - |
题
我正在使用”存根技术“ 至 更新 我的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..
}
如您所见,到处都有代码气味。 :)
几点:
- 我使用依赖注入(基于接口)的所有内容
- 我使用工作模式单位来抽象objectContext并在多个存储库之间提供持久性
- 目前是我的 iunitofwork 接口只有1种方法:
void Commit();
- 控制器有
IUserContentService
和IUnitOfWork
注射di IUserContentService
呼叫Find
在存储库中,使用ObjectContext
.
这是我不喜欢上述代码的两件事:
- 我不想施放 iunitofwork 作为
MySqlServerObjectContext
. - 我不希望控制器必须关心
ApplyCurrentValues
我基本上希望我的代码看起来像这样:
[HttpPost]
public ActionResult Edit(Review review)
{
_userContentService.Update(review);
_unitOfWork.Commit();
// ..snip - MVC stuff..
}
有什么想法,我该怎么做? (或类似的东西)。
我已经有聪明的人可以根据类型(仿制药的组合,复数化)来制定实体设置名称,因此请不要为此担心太多。
但 我想知道最好的地方 ApplyCurrentValues
是?将其放在 IUnitOfWork
接口,因为这是一个持久性(EF)的问题。出于同样的原因,它不属于服务。如果我把它放在我的 MySqlServerObjectContext
班级(有道理),我会从哪里称呼,因为没有什么可以直接可以访问此类 - 当某物请求时,它会通过DI注入 IUnitOfWork
.
有什么想法吗?
编辑
我在下面使用存根技术有一个解决方案,但是问题是,如果我已提前检索了要更新的实体,则会引发异常,并说明已经存在该密钥的实体。
这是有道理的,尽管我不确定如何解决这个问题?
我是否需要“检查实体是否已经连接,如果没有附加,请附加?”
那里的EF4专家可以提供帮助吗?
编辑
没关系 - 找到解决方案,请参见下面的答案。
解决方案
弄清楚了 - 这并不容易,所以我会尽力解释我能做到的。 (对于那些关心的人)
控制器相关代码:
// _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);
}
}
而且因为班级 衍生 从 通用物, ,它继承了所有核心存储库逻辑(查找,添加等)。
起初,我试图把那个 UpdateModel 代码 通用物 类本身(然后我不需要这个特定的存储库),但是问题是检索现有实体的逻辑是基于特定实体密钥, GenericRepository<T>
不知道。
但最终结果是 缝合 隐藏在数据层深处的深处,我最终得到了一个非常干净的控制器。
编辑
这种“存根技术”也有效:
public void UpdateModel(Post post)
{
var stub = new Review {PostId = post.PostId};
CurrentEntitySet.Attach(stub);
Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}
但是问题是因为 邮政 是抽象的,我不能实例化,因此必须检查帖子的类型并为 每一个 派生类型。并不是真正的选择。
编辑2(上次)
好的,获得了与抽象类合作的“存根技术”,因此现在解决了并发问题。
我在我的 UpdateModel 方法和特殊 新()约束.
执行:
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(好的 - 就是这样!)
我找到了解决方案,这要归功于这个答案: 是否可以检查实体框架中的数据上下文是否已经连接到对象?
我曾尝试使用以下代码“检查实体是否已连接”:
ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);
但是它总是返回 无效的, ,即使在探索OSM的过程中,我也可以看到我的实体使用相同的钥匙。
但是此代码有效:
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)
也许是因为我正在使用Pure Poco,所以OSM难以弄清楚实体密钥,谁知道。
哦,还有我添加的另一件事 - 这样我就不必为每个实体添加一个特定的存储库,我创建了一个称为“EntityKey”(公共财产属性)。
所有POCO都必须拥有1个带有该属性的公共财产,否则我在存储库模块中抛出了一个例外。
因此,我的通用存储库然后寻找此属性,以创建/设置存根。
是的 - 它使用反射,但它是巧妙的反射(基于属性),我已经在使用反射来对T。
无论如何,问题解决了 - 现在一切正常!