Entity Framework - Eagerly load object graph using stored procedures

前端 未结 3 1136
情歌与酒
情歌与酒 2021-02-06 10:11

Background

I am changing my LINQ-to-SQL code in my project to Entity Framework. Most of the change over was relatively simple, however, I have run into

相关标签:
3条回答
  • 2021-02-06 10:27

    It can be done in a fairly simple way but takes some manual effort. Here is an MSDN post on handling stored procedures with multiple result sets which shows both a code first and database first approach.

    Example:

    Load EntityB proc:

    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
    

    Load EntityA proc (which calls 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
    

    Entity classes

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

    Method that calls sproc and handles results (for this method, you could return the one EntityA if you'd like)

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

    Usage:

    EagerLoadEntityA( 1234 );
    var entityA = db.EntityAs.Find( 1234 ); // cached
    var primB = entityA.PrimaryEntityB; // this is already loaded
    
    0 讨论(0)
  • 2021-02-06 10:29

    Stored procedures cannot be directly associated with an Entity for the purposes of selecting / reading data. Typically stored procedures that are used for retrieval will return complex types and not entities. In order to interact with the DB indirectly, EF provides the ability to associate an Entity with a View for reads, and stored procedures for Insert, Update, and Delete.

    Read this article for a full summary of working with EF and stored procedures. http://msdn.microsoft.com/en-us/data/gg699321.aspx

    0 讨论(0)
  • 2021-02-06 10:32

    Okay, so after even further deliberation, I figured out a solution that works for what I am wanting. Since I am in a web environment and have no need to lazily load objects, I turned EnableLazyLoading to false for the entire DbContext. Then, using an EF feature called the magical relationship fix-up, I am able to do the following:

    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.
    
    0 讨论(0)
提交回复
热议问题