【问题标题】:Entity Framework Core Explicitly loading related data according to Type of ClassEntity Framework Core 根据类的类型显式加载相关数据
【发布时间】:2018-01-18 14:00:05
【问题描述】:

我有包含的 Repository 类。下面的代码适用于一对一的关系。但是对于收藏我需要将DbContext.Entry(model.Result).Reference(include).Load(); 更改为DbContext.Entry(model.Result).Collection(include).Load();

    public virtual async Task<TEntity> GetByIdAsync(object[] keyValues,
        List<Expression<Func<TEntity, object>>> includes,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var model = DbSet.FindAsync(keyValues, cancellationToken);
        if (includes == null) return await model;
        foreach (var include in includes)
            //if (include.Body.Type.IsAssignableFrom(typeof(ICollection<>)))
            DbContext.Entry(model.Result).Reference(include).Load();

        return await model;

        //return await DbSet.FindAsync(keyValues, cancellationToken);
    }

我可以在这里使用什么样的条件来将引用和集合彼此分开?

谢谢。

编辑: 示例对象是

System.Collections.Generic.IList`1[[WestCore.Domain.Entities.WestLife.MhpProduct]]

一般来说,集合可以是ICollection 或IList。

【问题讨论】:

    标签: c# entity-framework .net-core entity-framework-core


    【解决方案1】:

    您可以使用更通用的Navigation 方法,而不是单独的Reference / Collection 方法。不幸的是,它没有 lambda 表达式的重载,因此您需要手动提取属性名称。由于您的方法设计不支持嵌套属性,您可以从 lambda 表达式主体中获取该信息,转换为 MemberExpression

    foreach (var include in includes)
    {
        var propertyName = ((MemberExpression)include.Body).Member.Name;
        DbContext.Entry(model.Result).Navigation(propertyName).Load();
    }
    

    【讨论】:

    • 当我使用这种方法时,我发现实体框架正在生成错误的 SQL SELECT "e".MP_MHP_ID, "e".MP_PRODUCT_ID, "e".MP_G_ORDER, "e".G_END_DATE, "e".G_INSERT_BY, "e".G_INSERT_DATE, "e".G_IS_DELETED, "e".G_START_DATE, "e"."MP_WEST_CORE._DOMAIN._ENTITIES._WEST_LIFE._MHP_PRODUCT" FROM MHP_PRODUCT "e" WHERE "e".MP_PRODUCT_ID = :p__get_Item_0 请注意我正在使用 Devart.Oracle.DataAccess
    • 我不明白这是怎么发生的,因为它使用的方法与 Reference /Collection 调用内部使用的方法相同。我看到了您并接受了“答案”,但他们使用热切加载,而问题中的代码用于显式加载。换句话说,他们回答了没有被问到的不同问题。当然Include 直接支持表达式,这很明显并且在文档中指定,因此不值得 SO Q/A。
    • 我实际上仍在考虑答案,因为@GPW 的答案似乎也给我带来了麻烦。我正在做。您的回答非常直截了当,我对 EF 生成的查询感到困惑。我正在做。我会更新 EF Core 和 Devart 看看会发生什么
    • 顺便说一句,您正在监控什么查询,它有什么问题? EF Core 处理相关数据的方式与 EF6 不同。通过预先加载,它对每个集合 Include 使用单独的查询。通过显式加载,它将在每个Load 调用中执行单独的查询。在这两种情况下都不会是一个查询。
    • 现在查询是这样的:查看我的答案SELECT "product.MhpProducts".MP_MHP_ID, "product.MhpProducts".MP_PRODUCT_ID, "product.MhpProducts".MP_G_ORDER, "product.MhpProducts".G_END_DATE, "product.MhpProducts".G_INSERT_BY, "product.MhpProducts".G_INSERT_DATE, "product.MhpProducts".G_IS_DELETED, "product.MhpProducts".G_START_DATE, "product.MhpProducts"."MP_WEST_CORE._DOMAIN._ENTITIES._WEST_LIFE._MHP_PRODUCT" FROM MHP_PRODUCT "product.MhpProducts" INNER JOIN ( SELECT "product0".TP_ID FROM TREE_PRODUCT "product0" ) "t" ON "product.MhpProducts".MP_PRODUCT_ID = "t".TP_ID ORDER BY "t".TP_ID
    【解决方案2】:

    我会改变它,而不是明确模仿 FindByIdAsync 我会有一个更灵活的方法:

    public async Task<T> GetItemAsync<T>(Expression<Func<T, bool>> filter, 
              List<Expression<Func<T,object>>> includes) where T: class
    {
        var model = Set<T>();
        foreach (var include in includes)
        {
            model.Include(include);
        }
        return await model.FirstOrDefaultAsync(filter);
    }
    

    然后你会打电话给:

    var result = GetItemAsync<MyEntity>(x => x.Id == 3, 
                 new List<Expression<Func<MyEntity, object>>> 
                 { 
                     x => s.SomeProperty, 
                     x => s.SomeOtherProperty
                 });
    

    这与您的非常相似,但更灵活一些,并且只会访问数据库一次,而不是像我相信您现有的代码那样懒惰地单独加载所有内容。这可能是您的意图,但我们没有这样的背景。

    另外 - 调用 model.Result 并使用非 asyc Load() 方法意味着您的代码对异步的友好程度低于预期...

    【讨论】:

      【解决方案3】:

      编辑:

      我发现我仍然有问题。是

      public virtual async Task<TEntity> GetByIdAsync(object[] keyValues,
              List<Expression<Func<TEntity, object>>> includes,
              CancellationToken cancellationToken = default(CancellationToken))
          {
              Task<TEntity> model = null;
      
              foreach (var include in includes)
              {
                  if (include.Body.Type.GetInterface(nameof(IEnumerable<TEntity>)) != null)
                  { //this part generates SQL Below
                      await DbSet.Include(include).LoadAsync(cancellationToken);
                      model = DbSet.FindAsync(keyValues, cancellationToken);
                  }
                  else //this part is working
                  {
                      var propertyName = ((MemberExpression)include.Body).Member.Name;
                      model = DbSet.FindAsync(keyValues, cancellationToken);
                      await DbContext.Entry(model.Result).Navigation(propertyName).LoadAsync(cancellationToken);
                  }
              }
      
              if (model == null)
                  model = DbSet.FindAsync(keyValues, cancellationToken);
      
              return await model;
          }
      

      IEnumerable 部分生成下面的SQL:

      选择“product.MhpProducts”.MP_MHP_ID、“product.MhpProducts”.MP_PRODUCT_ID、“product.MhpProducts”.MP_G_ORDER、“product.MhpProducts”.G_END_DATE、“product.MhpProducts”.G_INSERT_BY、“product.MhpProducts”。 G_INSERT_DATE, "product.MhpProducts".G_IS_DELETED, "product.MhpProducts".G_START_DATE, "product.MhpProducts"."MP_WEST_CORE._DOMAIN._ENTITIES._WEST_LIFE._MHP_PRODUCT" FROM MHP_PRODUCT "product.MhpProducts" 内部联接 ( 选择“product0”.TP_ID FROM TREE_PRODUCT "product0" ) "t" ON "product.MhpProducts".MP_PRODUCT_ID = "t".TP_ID ORDER BY "t".TP_ID

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-16
        • 1970-01-01
        • 2021-12-03
        • 2023-04-02
        • 2019-02-23
        • 2017-08-01
        • 1970-01-01
        • 2018-03-19
        相关资源
        最近更新 更多