Ранее (при использовании .net 4.5.2 и EF 6). У меня был общий метод Get
, который допускал ряд включений, как показано ниже;
public abstract class DataContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IDataContext
{
public DataContext(DbContextOptions options)
: base(options)
{
}
// reduced for brevity
public T Get<T>(int id, params Expression<Func<T, object>>[] includes) where T : class, IEntity
{
return this.Set<T>().Include(includes).FirstOrDefault(x => x.Id == id);
}
Я бы тогда позвонил, например;
context.Get<Job>(id,
x => x.Equipment,
x => x.Equipment.Select(y => y.Type));
Чтобы включить Job.Equipment
, а также Job.Equipment.Type
.
Однако, когда я перенес это на ядро asp.net 2. Я попробовал тот же общий подход, но если я попытаюсь включить подобъект, я получаю следующую ошибку;
The property expression 'x => {from Equipment y in x.Equipment select [y].Type}' is not valid. The expression should represent a property access: 't => t.MyProperty'. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
Может ли кто-нибудь предложить, как я могу обойти это, чтобы включить дочерние объекты в мой общий метод Get<T>
с Entity Framework Core 2?
Обновлять
При просмотре документов есть дополнительный метод включения
include(string navigationPropertyPath)
Я добавил следующий метод;
public T Get<T>(int id, string[] includes) where T : class, IEntity
{
var result = this.Set<T>().AsQueryable();
foreach(var include in includes)
{
result = result.Include(include);
}
return result.FirstOrDefault(x => x.Id == id);
}
Что работает, хотя я не уверен в эффективности здесь?
.Include
работает правильно, но кажется, что выбор суб-сущностей изменился в ядре 2? (код, который работал нормально, по-видимому, не работает). Также я нашел ссылку docs.microsoft.com/en-us/ef/core/querying/related-data, которая, кажется, предлагает .ThenInclude
Да, вам нужно использовать ThenInclude
, но ваш код даже не компилируется для меня на EF Core 2
Странно, что это для меня компилируется без проблем. Я редактирую, чтобы включить мое расширение DbContext
. Я заметил, что есть Include(string navigationPropertyPath)
, который, возможно, справится с этой задачей. Я провожу это испытание и обновлю свои результаты.
Я могу подтвердить, что использование вышеуказанного метода действительно работает. Однако я не уверен в эффективности моего метода здесь? (я обновил вопрос, чтобы перечислить его)
Эффективность в порядке. Просто типовой безопасности (как и у любого string
) нет.
@IvanStoev Вы порекомендуете что-нибудь, чтобы попытаться осветить это?
Другое решение - преобразовать лямбда-выражения в путь в виде строки: stackoverflow.com/a/47063432/861716
Шаблон EF Core Include
/ ThenInclude
не может быть представлен Expression<Func<T, object>>[]
, как в EF6.
Взглянув на исходный код одного из расширений EF Core - Microsoft.EntityFrameworkCore.UnitOfWork, который утверждает, что
A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported.
похоже, что предполагаемый шаблон для включений должен быть основан на Func<IQueryable<T>, IIncludableQueryable<T, object>>
:
public T Get<T>(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null) where T : class, IEntity
{
var result = this.Set<T>().AsQueryable();
if (include != null)
result = include(result);
return result.FirstOrDefault(x => x.Id == id);
}
Недостатком является то, что он добавляет зависимость EF Core от вызывающей стороны и требует using Microsoft.EntityFrameworkCore;
. Что в вашем случае несущественно, поскольку вы расширяете DbContext
.
Использование с вашим примером будет:
context.Get<Job>(id, q => q
.Include(x => x.Equipment)
.ThenInclude(y => y.Type));
Допустит ли это несколько включений? например, если мне нужно включить в возврат несколько сложных объектов? Как и в случае с EF6, я мог бы просто связать включения, чтобы вернуть то, что мне нужно?
Да, это так. В EF Core вы объедините несколько Include
/ ThenInclude
. Каждый Include
перезагружается из корневого каталога. Пример здесь и в документации.
Как вы думаете сделать это динамичным? Я хотел бы иметь это в BaseRepository, который является базовым классом некоторых репозиториев, и я бы не стал писать все реализации для каждого репо, но я не могу найти способ сделать thenInclude динамическим, какие-либо предложения?
@RSadocchi Не уверен, что вы имеете в виду под словом «сделать динамическим», возможно, вы могли бы привести пример. В приведенном выше примере (и ссылке) Include
/ ThenInclude
предоставляется извне с помощью звонящий метода репо.
@IvanStoev спасибо за ваш ответ, пожалуйста, посмотрите stackoverflow.com/questions/55898559/… для примера кода
Вы можете сделать что-то вроде этого:
public abstract class DataContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IDataContext
{
public DataContext(DbContextOptions options)
: base(options)
{
}
// reduced for brevity
public T Get<T>(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes = null) where T : class, IEntity
{
IQueryable<T> queryable = this.Set<T>();
if (includes != null)
{
queryable = includes(queryable);
}
return queryable.FirstOrDefault(x => x.Id == id);
}
}
context.Get<Job>(id, includes: source => source.Include(x => x.Equipment).ThenInclude(x => x.Type));
Нет перегрузки
Include
, которая принимаетExpression<Func<T, object>>[]
в Entity Framework Core, или это то, как вы ее используете в EF6?