Об этом много раз спрашивали с момента первого выпуска EF Core.Более ранние предварительные версии EF Core даже поддерживали его, но затем он был удален из кода EF Core (я думаю, чтобы продвигать новый шаблон Include
/ ThenInclude
).
Пока Include
/ ThenInclude
шаблон выглядит более четким (помимо текущих проблем с Intellisense), у него есть один существенный недостаток - требуется доступ к EntityFrameworkQueryableExtensions
, то есть ссылка на Microsoft.EntityFrameworkCore
сборку.В то время как params
Expression> `pattern не имеет такого требования.
Хорошо, что можно относительно легко добавить эту функциональность.Исходный код EF6 общедоступен на GitHub, и оттуда мы видим, что он использует метод с именем TryParsePath для построения пути строки, разделенного точками, который затем передается перегрузке string
метода Include
.
То же самое можно применить в EF Core.Возможно, мы можем использовать код EF6, но я собираюсь предоставить свою собственную версию.Легко видеть, что поддерживаемые конструкции являются членами-членами или обращаются к методу, называемому Select
с двумя аргументами, вторым является LambdaExpression
.
Ниже приводится моя интерпретация вышеизложенного, заключенная в два пользовательских метода расширения:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Microsoft.EntityFrameworkCore
{
public static class IncludeExtensions
{
public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<string> includePaths) where T : class
=> includePaths.Aggregate(source, (query, path) => query.Include(path));
public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, object>>> includePaths) where T : class
=> source.Include(includePaths.Select(e => GetIncludePath(e?.Body)));
static string GetIncludePath(Expression source, bool allowParameter = false)
{
if (allowParameter && source is ParameterExpression)
return null; // ok
if (source is MemberExpression member)
return CombinePaths(GetIncludePath(member.Expression, true), member.Member.Name);
if (source is MethodCallExpression call && call.Method.Name == "Select"
&& call.Arguments.Count == 2 && call.Arguments[1] is LambdaExpression selector)
return CombinePaths(GetIncludePath(call.Arguments[0]), GetIncludePath(selector.Body));
throw new Exception("Invalid Include path.");
}
static string CombinePaths(string path1, string path2)
=> path1 != null ? path1 + "." + path2 : path2;
}
}
Первый - просто помощник для вызова нескольких включений string
(взято из моего ответа на Entity Framework Core 2.0.1 Eager Загрузка всех вложенных связанных сущностей ).Второй - это рассматриваемый метод, который преобразует выражения в строки и вызывает первый.Основная работа выполняется GetIncludePath
приватным методом, который рекурсивно обрабатывает выражение на основе вышеупомянутых правил, плюс одно дополнительное правило - при переходе снизу вверх он должен заканчиваться параметром lambda.
Теперь реализацияМетод - это простой вопрос:
public IQueryable<T> OldMethod(params Expression<Func<T, object>>[] includeProperties)
=> set.Include(includeProperties);