EF Core 2.0 включает вложенные сущности с динамическим запросом

Я использую System.Linq.Dynamic.Core; и следующий метод расширения для динамического доступа к DbSet по имени и генерации запросов из строк.

Это метод расширения:

namespace Microsoft.EntityFrameworkCore
{
    public static partial class CustomExtensions
    {
        public static IQueryable Query(this DbContext context, string entityName) =>
            context.Query(context.Model.FindEntityType(entityName).ClrType);

        public static IQueryable Query(this DbContext context, Type entityType) =>
            (IQueryable)((IDbSetCache)context).GetOrAddSet(context.GetDependencies().SetSource, entityType);
    }
}

Вот как я получаю доступ к DbSet:

IQueryable<T> dbSet = (IQueryable<T>)_db.Query(entityName);

Это отлично работает, я могу построить запрос, а затем создать список, но ни один из вложенных объектов не загружается. Кажется, IQueryable не имеет определения для Include(). Я вижу метод включения, если я напрямую обращаюсь к контексту БД обычным способом, но не использую этот динамический метод.

Как включить вложенные объекты с помощью динамического метода?


person Guerrilla    schedule 01.01.2018    source источник


Ответы (2)


Во-первых, вы используете GetOrAddSet из IDbSetCache

public interface IDbSetCache

// // Резюме: // Этот API поддерживает инфраструктуру Entity Framework Core и не предназначен // для использования непосредственно из вашего кода. Этот API может быть изменен или удален в будущем.

Include - это метод IQueryable<TEntity> в классе EntityFrameworkQueryableExtensions класса Microsoft.EntityFrameworkCore, а не для IQueryable. Ваша функция возвращает IQueryable

Я бы порекомендовал вам создать метод расширения, как показано ниже,

        public static IQueryable<T> MyQuery<T>(this DbContext context)
            where T : class
        {
            return context.Set<T>().AsQueryable();
        }

И вы можете потреблять,

  var result = _dbContext.MyQuery<Employee>().Include("Department");

Для динамического включения,

    /// <summary>
    /// Query with dynamic Include
    /// </summary>
    /// <typeparam name="T">Entity</typeparam>
    /// <param name="context">dbContext</param>
    /// <param name="includeProperties">includeProperties with ; delimiters</param>
    /// <returns>Constructed query with include properties</returns>
    public static IQueryable<T> MyQueryWithDynamicInclude<T>(this DbContext context, string includeProperties)
       where T : class
    {            
        string[] includes = includeProperties.Split(';');
        var query = context.Set<T>().AsQueryable();

        foreach (string include in includes)
            query = query.Include(include);

        return query;
    }
person programtreasures    schedule 01.01.2018
comment
Это было действительно полезно, спасибо! Одно возможное изменение; вместо того, чтобы включать несколько имен свойств в одну и ту же строку и разделять их внутри метода, вместо этого вы можете использовать params string[] includeProperties в качестве последнего параметра в своем методе. - person Klicker; 21.04.2020
comment
Что, если мне также нужно включить подсущности? Входным параметром является params string[] includeProperties, поэтому я бы передал new[] { nameof(SomeClass.MyProperty), nameof(...) ... }, но тогда, если MyProperty является списком, и я хочу включить его подсвойство, возникает проблема... - person kkamil4sz; 01.09.2020

У меня есть код для этой задачи. Я использую дерево выражений.

Моя модель объекта, как показано ниже:

public class PortfolioTechnology
{
    public int PortfolioId { get; set; }
    public Portfolio Portfolio { get; set; }

    public int TechnologyId { get; set; }
    public Technology Technology { get; set; }
}

Мой основной код, как показано ниже:

public SharpListResponse<PortfolioTechnology> GetAll(
    Expression<Func<PortfolioTechnology, bool>> predicate,
    params Expression<Func<PortfolioTechnology,object>>[] includes)
{
    var query = _dbContext.PortfolioTechnology.AsQueryable();

    foreach (var include in includes)
    {
        var memberExpression = include.Body as MemberExpression;

        if (memberExpression != null)
            query = query.Include(memberExpression.Member.Name);
    }

    var result = query.Where(predicate).ToList();

    return new SharpListResponse<PortfolioTechnology>(result);
}

И используйте этот метод, как показано ниже:

var list = _unitOfWork.PortfolioTechnologyRepository.GetAll(x => x.PortfolioId == id, 
                                                            y => y.Technology);

Если вы хотите включить несколько объектов, например, включить объекты Portfolio и Technology, ваш код выглядит следующим образом:

 var list = _unitOfWork.PortfolioTechnologyRepository.GetAll(x => x.PortfolioId == id, 
                                                             y => y.Technology,
                                                             x => x.Portfolio);

Примечание. SharpListResponse является классом-оболочкой. Код работает и без него

Дополнительные сведения о SharpListResponse: https://www.nuget.org/packages/SharpRequestResponseWrapper/

person Ramil Aliyev    schedule 22.03.2020
comment
Замечательное решение. Гораздо удобнее, чем использовать строки. - person D2TheC; 11.11.2020