背景
我将项目中的LINQ-to-SQL代码更改为Entity Framework。大部分更改都相对简单,但是,我遇到了一个相当大的问题。使用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,EF仍在尝试为其获取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的所有者为null时,不允许执行请求的操作。使用默认构造函数创建的RelatedEnd对象仅应在序列化期间用作容器。”
在广泛研究了每个错误之后,我仍然找不到与我的问题有关的解决方案。
问题
因此,毕竟,我的问题是:当每个对象都使用Entity Framework 4拥有自己的存储过程时,如何加载整个对象图?
更新
因此,根据目前为止的答案,我觉得我需要在此处包括以下警告:
我不是在寻找使用单个存储过程来加载整个对象图的答案。我正在寻找一种使用每个实体的get存储过程来加载对象图的方法。我意识到,使用单个存储过程加载对象图在理论上可以实现更好的性能,但是现在,我对代码库的较小更改(尤其是数据库的结构方式)更感兴趣。
如果您的解决方案需要直接编辑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();
好的,因此,在进一步考虑之后,我想出了一种适用于我想要的解决方案。由于我处于Web环境中,不需要延迟加载对象,因此对于整个DbContext,我都将EnableLazyLoading设置为false。然后,使用称为魔术关系修复的EF功能,可以执行以下操作:
ViewModel.Model = MyDbContext.usp_ModelA_GetByID(AId).Single();
var 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();
//ToList() executes the proc and projects the plate details into the object
//graph which never tries to select from the database because LazyLoadingEnabled is
//false. Then, the magical relationship fix-up allows me to traverse my object graph
//using ViewModel.Model.ModelBs which returns all of the ModelBs loaded into the graph
//that are related to my ModelA.
问题内容: 我已经创建了这个并且尝试使用实体框架来调用它。下面是用C#编写的代码。 在所有情况下都返回。请让我知道错误。 编辑 -根据给定的答案,不使用存储过程的return语句,因为Entity Framework不支持开箱即用的存储过程返回标量值。让我知道如何从中发送标量数据? 问题答案: 您的存储过程当前返回 标 量值。使用以下步骤来解决此问题: 像这样更改您的存储过程(不要在存储过程中使用
我定义了两个具有一对多外键关系的类,它们首先由数据库生成。 我创建了一个代理对象,并将其添加到DBContext中。 然后我设置了一个外键属性并尝试访问相应的导航属性。 设置外键属性标签\u id不会加载导航属性标签。代码中的最后一行返回。 这些类遵循此处列出的创建POCO代理的要求(调试显示已创建代理car对象,因此我相信所有要求都已满足)。 数据库表标签包含主键为4的项。 我不是在这里调用或,
我目前正在使用实体框架来建立一个论坛使用ASP。NET MVC I有3个主要模型[类别]1--*[论坛]1--*[帖子] 如果我有5个类别,每个类别有4个论坛,每个论坛有1000个帖子,我只想显示类别,当我这样做的时候,这意味着我也选择了20000个帖子?? 因为每个类别对象都有一个列表
我在向数据库添加一个包含与现有对象关系的实体时遇到了麻烦。我搜索了很多,但找不到合适的解决方案。我会尽可能简单地描述这个。 在存储库中,我添加了这样的记录。这是泛型类 现在当我尝试添加这样的新记录时 商店关系被添加为新商店,并获得新ID。有人知道我如何解决这个问题吗? 数据库示例: 这是我关于stackoverflow的第一个问题。
我对Entity Framework 6非常陌生,我想在我的项目中实现存储过程。我有一个存储过程,如下所示: 班级: 我的问题是:如何调用存储过程并向其中传递参数?
问题内容: 我试图从存储过程中获取简单的输出 和我的MVC 但是当我编译时,在以下部分出现错误: 方法’sp_getrandomnumber’的重载没有接受1个参数 名称“ randoms”在当前上下文中不存在 提前非常感谢希什 问题答案: 您的问题是您的SPROC输出是一个int。 请参见Scott Gu的文章中的以下引用。 “ LINQ to SQL将SPROC中的“ out”参数映射为参考参