【问题标题】:Using navigation to load entity 2 level deep使用导航加载实体 2 级深度
【发布时间】:2015-09-05 10:57:33
【问题描述】:

我有课

public class Level1
{
   public int Id {get; set;}
   public virtual List<Level2> Level2List {get; set;}
}

public class Level2
{
   public int Id {get; set;}
   public int Level3Id {get; set;}
   public virtual Level3 Level3 {get; set;}
}

public class Level3
{
   public int Id {get; set;}
   public string Name {get; set;}
}

使用导航属性我可以像这样加载List&lt;Level2&gt;

var myList = _myLevel1Repository.GetSingle(x=>x.Id == Level1Id, x=>x.Level2List);

但是如何加载 Level3 及其与 Level2 链接的属性?

PS:不能延迟加载。 这是Getsingle 函数

    public T GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navProps)
    {
        T item = null;
        using (var context = new MyDbContext())
            item = GetFilteredSet(context, navProps).AsNoTracking().FirstOrDefault(where);
        return item;
    }

【问题讨论】:

    标签: c# linq entity-framework linq-to-entities navigation-properties


    【解决方案1】:

    你的GetSingle方法应该是这样的:

    public T GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navProps)
    {
        T item = null;
        using (var context = new MyDbContext())
        {
            IQueryable<T> query = context.Set<T>();
    
            //Include the navigations properties as part of the query
            if (navProps!= null)
            {
                query = navProps.Aggregate(query, (current, include) => current.Include(include));
            }
            item = query.Where(where).FirstOrDefault();
        }
        return item;
    }
    

    我不知道你在GetFilteredSet 方法中做了什么,但我想你可以在方便时重新组织我上面显示的代码。包含多个级别的导航的关键。 EF 中的属性使用Include 方法。当您使用此方法时,您将加载导航。属性作为查询的一部分(检查此link 中的急切加载部分)。现在,有两个 Include 方法:

    • DbQuery.Include Method

      使用这个方法你需要传递导航的路径。您要作为字符串加载的属性,例如,在您的情况下,它将是:

      context.Set<Level1>.Include("Level2List.Level3");
      
    • DbExtensions.Include extension method

      这是我在上面的代码中使用的方法,您可以使用 lambda 表达式指定要包含的相关对象。恕我直言,这是最好的变体,因为它是强类型的,并且如果您更改了一些导航。实体中的属性名称,您还将收到编译错误。在我上面分享的link 中,您可以看到所有可用于包含不同级别导航的模式。属性。

      context.Set<Level1>.Include(l1=>l1.Level2List.Select(l2=>l2.Level3));
      

    回到最初的问题,现在您可以使用您的GetSingle 方法以这种方式包含多个级别:

    var entity= _myLevel1Repository.GetSingle(x=>x.Id == Level1Id, x=>x.Level2List.Select(l2=>l2.Level3));
    

    【讨论】:

    • 没错,我提到的第一个Include 方法并不是最好的选择。当您键入一个属性的名称时可能会出错,或者您也可能忘记在路径中重命名其中一个,以防您更改模型上的某些属性名称。
    • 好吧,就像你说的我改变了我的getsingle方法。你的最后一行代码就像魅力一样。但你能告诉我一件事吗?在这段代码var entity= _myLevel1Repository.GetSingle(x=&gt;x.Id == Level1Id, x=&gt;x.Level2List.Select(l2=&gt;l2.Level3)); 中,为什么使用.select(l2=&gt;l2.Level3) 中的l2 而不是x。我还能更深入吗?
    • 因为我需要从Level2 实体中选择一个属性(这将是您的第二级),所以x 是您的根。关于更深层次的负载水平,是的,您可以,在我分享的与第二个Include 相关的链接中,您会找到一个示例:query.Include(e =&gt; e.Level1Collection.Select(l1 =&gt; l1.Level2Collection.Select(l2 =&gt; l2.Level3Reference))).
    【解决方案2】:

    使用 include 怎么样?

     var mylevel1s = _db.Level1(x=>x.Id == Level1Id).Include(x=> x.Level2List.Select(a=>a.Level3));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多