gpt4 book ai didi

c# - Entity Framework -使用存储过程急于加载对象图

转载 作者:太空狗 更新时间:2023-10-29 21:44:46 26 4
gpt4 key购买 nike

背景

我将项目中的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。尽管即使添加了实体,我仍然不完全理解为什么要尝试加载实体,但是我只能假设,因为它本身未加载实体,所以不相信它们已完全加载,并且可能查看了所有实体。我将其加载为"new"的对象。

为了绕过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,那么它将是 Not Acceptable 答案。由于这是一个自动生成的文件,因此直接编辑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();

    最佳答案

    可以用相当简单的方式完成此操作,但需要一些人工。 Here is an MSDN post处理具有多个结果集的存储过程,该过程同时显示了代码优先和数据库优先的方法。

    例子:

    加载EntityB过程:

    create proc dbo.Get_EntityB_by_EntityAId( @aId int )
    as

    select distinct
    b.EntityBId
    , b.Description
    from
    EntityA a
    left outer join EntityB b
    on a.PrimaryEntityB_EntityBId = b.EntityBId
    left outer join EntityB b2
    on a.AlternativeEntityB_EntityBId = b2.EntityBId
    where
    a.EntityAId = @aId
    go

    加载EntityA proc(调用load B proc)
    create proc dbo.Get_EntityA_by_Id( @id int )
    as

    -- use a select statement
    select
    a.EntityAId
    , a.Description
    , a.PrimaryEntityB_EntityBId
    , a.AlternativeEntityB_EntityBId
    from
    EntityA a
    where
    a.EntityAId = @id

    -- and/or other sprocs
    exec dbo.Get_EntityB_by_EntityAId @id

    go

    实体类
    [Table("EntityA")]
    public partial class EntityA
    {
    public int EntityAId { get; set; }
    public string Description { get; set; }


    public virtual EntityB PrimaryEntityB { get; set; }

    public virtual EntityB AlternativeEntityB { get; set; }
    }


    [Table("EntityB")]
    public partial class EntityB
    {
    public int EntityBId { get; set; }
    public string Description { get; set; }

    [InverseProperty("PrimaryEntityB")]
    public virtual ICollection<EntityA> EntityAsViaPrimary { get; set; }
    [InverseProperty( "AlternativeEntityB" )]
    public virtual ICollection<EntityA> EntityAsViaAlternative { get; set; }
    }

    调用sproc并处理结果的方法(对于该方法,您可以根据需要返回一个 EntityA)
    public static void EagerLoadEntityA( int aId )
    {
    using( var db = new TestEntities() )
    {
    // if using code first
    db.Database.Initialize( false );

    var cmd = db.Database.Connection.CreateCommand();
    cmd.CommandText = "dbo.Get_EntityA_by_Id";

    db.Database.Connection.Open();

    try
    {
    var reader = cmd.ExecuteReader();

    var objContext = ( ( IObjectContextAdapter )db ).ObjectContext;

    var aEntities = objContext
    .Translate<EntityA>( reader, "EntityAs", MergeOption.AppendOnly );

    reader.NextResult();

    var bEntities = objContext
    .Translate<EntityB>( reader, "EntityBs", MergeOption.AppendOnly );

    }
    finally
    {
    db.Database.Connection.Close();
    }
    }
    }

    用法:
    EagerLoadEntityA( 1234 );
    var entityA = db.EntityAs.Find( 1234 ); // cached
    var primB = entityA.PrimaryEntityB; // this is already loaded

    关于c# - Entity Framework -使用存储过程急于加载对象图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21538728/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com