【发布时间】:2014-02-03 21:59:28
【问题描述】:
背景
我正在将项目中的 LINQ-to-SQL 代码更改为实体框架。大部分的改变都相对简单,但是,我遇到了一个相当大的问题。使用 LINQ-to-SQL,我能够使用如下存储过程加载整个对象图(用于模型 B):
ViewModel.Model = MyDbContext.usp_ModelA_GetByID(AId).Single();
List<ModelB> Details =
(from b in MyDbContext.usp_ModelB_GetByID(BId)
join c in MyDbContext.usp_ModelC_GetAll()
on b.CId equals c.CId
select new ModelB()
{
BId = b.BId,
CId = b.CId,
C = c
}).ToList();
ViewModel.Model.ModelBs.AddRange(Details);
但是,在将此代码转换为 EF 后,在访问 ViewModel.Model.ModelBs 的行上,我收到错误“EntityCommandExecutionException”,内部异常解释为“对象‘ModelBTable’的 SELECT 权限被拒绝。 "显然,EF 正在尝试获取 ModelA 的 ModelB,即使我已经从数据库中加载了它们。虽然我不完全理解为什么即使我添加了实体,它也试图加载它们,但我只能假设因为它本身没有加载它们,它不相信它们已完全加载并且可能查看所有我作为“新”加载到其中的对象。
为了绕过 EF 尝试获取对象本身,我决定将代码更改为:
ViewModel.Model = MyDbContext.usp_ModelA_GetByID(AId).Single();
List<ModelB> Details =
(from b in MyDbContext.usp_ModelB_GetByID(BId)
join c in MyDbContext.usp_ModelC_GetAll()
on b.CId equals c.CId
select new ModelB()
{
BId = b.BId,
CId = c.CId,
C = c
}).ToList();
ViewModel.Model.ModelBs = new EntityCollection<ModelB>();
foreach (ModelB detail in Details)
{
ViewModel.Model.ModelBs.Attach(detail);
}
进行此更改后,我现在遇到错误“InvalidOperationException”,并显示“无法初始化 EntityCollection,因为 EntityCollection 所属对象的关系管理器已附加到 ObjectContext。InitializeRelatedCollection 方法只应在对象图的反序列化期间调用以初始化新的 EntityCollection。"。
这很令人困惑,因为我使用相同的上下文来加载所有实体,所以我不确定为什么它不允许我将它们组合在一起。我可以在其他 ORM 中毫无问题地做到这一点。
在研究了这个错误之后,我决定尝试一种方法,我希望能欺骗 EF 认为整个对象图是由相同的上下文加载的,所以我将我的代码重写为:
ViewModel.Model =
(from a in MyDbContext.usp_ModelA_GetByID(AId)
select new A()
{
AId = a.AId,
ModelBs = (from b in MyDbContext.usp_ModelB_GetByID(BId)
join c in MyDbContext.usp_ModelC_GetAll()
on b.CId equals c.CId
select new ModelB()
{
BId = b.BId,
CId = b.CId,
C = c
}).ToEntityCollection()
}).Single();
ToEntityCollection 是我这样创建的扩展方法:
public static EntityCollection<TEntity> ToEntityCollection<TEntity>(
this IEnumerable<TEntity> source) where TEntity : class, IEntityWithRelationships
{
EntityCollection<TEntity> set = new EntityCollection<TEntity>();
foreach (TEntity entity in source)
{
set.Attach(entity);
}
return set;
}
现在,我收到错误“InvalidOperationException”,并显示“当此 RelatedEnd 的所有者为空时,不允许请求的操作。使用默认构造函数创建的 RelatedEnd 对象只能在序列化期间用作容器。 ”。
在深入研究了这些错误中的每一个后,我仍然无法找到与我的问题相关的解决方案。
问题
所以,毕竟,我的问题是:当每个对象都有自己的使用 Entity Framework 4 的存储过程时,我如何加载整个对象图?
更新
因此,根据目前的回答,我觉得我需要在此处包含以下注意事项:
-
我不是在寻找使用单个存储过程来加载整个对象图的答案。我正在寻找一种使用每个实体获取存储过程来加载对象图的方法。我意识到使用单个存储过程加载对象图理论上可以执行得更好,但此时,我对代码库的较小更改更感兴趣,尤其是在数据库结构方面。
如果您的解决方案需要直接编辑 edmx,这将不是一个可接受的答案。由于这是一个自动生成的文件,直接编辑 edmx 本质上意味着在通过设计器进行任何修改时都需要重新进行相同的更改。
更新 2
所以,经过深思熟虑,我想出了一个解决方法。我所做的是将我的 ViewModel 更改为具有使用存储过程连接提取数据的 List ModelBs 属性,在我看来,我只是将此属性设置为数据源。这绝对不是我认为的最佳解决方案,因为现在我的 ViewModel 更像是 Model 而不是 ViewModel,我不能再遍历我的 ModelA 类型来获取 ModelB 的列表,但它可以工作!我仍然不明白为什么我可以这样做:
(from b in MyDbContext.usp_ModelB_GetByID(BId)
join c in MyDbContext.usp_ModelC_GetAll()
on b.CId equals c.CId
select new ModelB()
{
BId = b.BId,
CId = b.CId,
C = c //<------Setting a navigation property and EF figures out that it belongs
}).ToList();
但我做不到:
(from a in MyDbContext.usp_ModelA_GetByID(AId)
select new ModelA()
{
AId = a.AId,
ModelBs = MyDbContext.usp_ModelB_GetByID(BId).ToEntityCollection() //<----Won't let me set the navigation property when the navigation property is a collection.
}).Single();
【问题讨论】:
标签: c# sql sql-server linq entity-framework