实体框架核心根据类的类型显式加载相关数据

时间:2018-01-18 14:00:05

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

我有包含的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。

3 个答案:

答案 0 :(得分:4)

您可以使用更通用的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();
}

答案 1 :(得分:1)

我改变它而不是明确地模仿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()方法意味着您的代码不像它那样异步友好...

答案 2 :(得分:0)

编辑:

我发现我仍然遇到问题。是

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:

  

SELECT&#34; product.MhpProducts&#34; .MP_MHP_ID,&#34; product.MhpProducts&#34; .MP_PRODUCT_ID,&#34; product.MhpProducts&#34; .MP_G_ORDER,&#34; product.MhpProducts& #34; .G_END_DATE,&#34; product.MhpProducts&#34; .G_INSERT_BY,&#34; product.MhpProducts&#34; .G_INSERT_DATE,&#34; product.MhpProducts&#34; .G_IS_DELETED,&#34;产品.MhPProducts&#34; .G_START_DATE,&#34; product.MhpProducts&#34;。&#34; MP_WEST_CORE._DOMAIN._ENTITIES._WEST_LIFE._MHP_PRODUCT&#34;         来自MHP_PRODUCT&#34; product.MhpProducts&#34;         内部联接 (             SELECT&#34; product0&#34; .TP_ID             来自TREE​​_PRODUCT&#34; product0&#34;         )&#34; t&#34; ON&#34; product.MhpProducts&#34; .MP_PRODUCT_ID =&#34; t&#34; .TP_ID         ORDER BY&#34; t&#34; .TP_ID