Entity Framework множественное включение во время выполнения

У меня есть служба, которая передает параметры того, сколько я хочу включить в свойства навигации. На основе логических аргументов он объединяет список сущностей, чтобы включить каждую требуемую внешнюю сущность.

Во время выполнения я хочу либо не включать элементы навигации, либо включать их множество. Что я не могу сделать, так это шлейфовое соединение с .Include().Include, поскольку я не знаю, что и сколько включить, основываясь на переданных в аргументах.

Я хочу добиться этого, но, похоже, я не могу передать список объектов, разделенных запятыми. Любые идеи?

var res = db.Entity.Include(entityListCommaSeparated).Where(_=>_.ID == ID).FirstOrDefault();

Как упоминает ниже Стив Пи, я бы предложил использовать лямбда-выражения, а не магические строки для include. Если вам когда-либо придется проводить рефакторинг, вы получите ошибки времени компиляции, а не более неясные ошибки базы данных времени выполнения (или, что еще хуже, никаких ошибок и включений не будет).

Wurd 04.09.2018 11:50

Я не мог использовать лямбда-выражения в этом случае, так как мне нужно было изменить включения во время выполнения на основе логических параметров включения. то есть 15 булевых значений со значением по умолчанию false, которые могут включать любую комбинацию соответствующих свойств навигации. С лямбда-выражениями 2 ^ 15 (32768) отдельных лямбда-выражений для поддержки всех возможных комбинаций включений. т.е. GetPolicy (int id, bool includeEntityA = false, bool includeEntityB = false, ....

SeanK 04.09.2018 20:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
130
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это похоже на шаблон репозитория и обычно становится беспорядочным, если вы хотите попытаться «скрыть» EF / DbContext от вызывающего кода.

Вы можете рассмотреть несколько вариантов:

  1. Вниз по сложности: используйте params Expression<Func<TEntity, object>>[] includes в подходящих методах репозитория, а затем будьте готовы также передавать выражения OrderBy, а также значения разбивки на страницы, когда вы хотите вернуть несколько сущностей.
  2. Через зеркало простоты: используйте IQueryable как возвращаемый тип и позвольте потребителям обрабатывать Includes, OrderBy's, Counts / Any / Skip / Take / First / ToList и .Select() по мере необходимости.

Опция 1:

public Order GetById(int id, params Expression<Func<Order, object>>[] includes)
{
     var query = db.Orders.Where(x => x.ID == id);
     // This part can be moved into an extension method or a base repository method.
     if (includes.Any)  
        includes.Aggregate(query, (current, include) => 
        {
            current.Include(include);
        }
     // Don't use .FirstOrDefault() If you intend for 1 record to be returned, use .Single(). If it really is optional to find, .SingleOrDefault()
     return query.Single();
}
//ToDo
public IEnumerable<Order> GetOrders(/* criteria?, includes?, order by?, (ascending/descending) pagination? */)
{ }
// or
public IEnumerable<Order> GetOrdersByCustomer(/* includes?, order by?, (ascending/descending) pagination? */)
{ }
// plus..
public IEnumerable<Order> GetOrdersByDate(/* includes?, order by?, (ascending/descending) pagination? */)
{ }
public bool CustomerHasOrders(int customerId)
{ }
public bool OrderExists(int id)
{ }
public int OrdersOnDate(DateTime date)
{ }
// etc. etc. etc.

Имейте в виду, что это не обрабатывает настраиваемый порядок по предложениям, и то же самое потребуется для методов, возвращающих списки сущностей. Ваш репозиторий также должен будет предоставлять методы для .Any() (DoesExist), потому что каждый любит проверяет #null при каждом возврате. :) Тоже .Count().

Вариант 2:

public IQueryable<Order> GetById(int id)
{
    return db.Orders.Where(x => x.ID == id);
}
public IQueryable<Order> GetOrders()
{
    return db.Orders.AsQueryable();
}

Вызывающие могут найти в Linq и .Include() то, что им нужно, прежде чем вызывать .Single(), или выполнить .Any(). Им может не понадобиться весь граф сущностей, поэтому они могут .Select() из сущности и связанных сущностей без .Include(), чтобы составить и выполнить более эффективный запрос для заполнения ViewModel / DTO. GetById может использоваться во многих местах, чтобы мы могли уменьшить дублирование и поддерживать его в репозитории. Нам не нужны все сценарии фильтрации и т. д., Вызывающие абоненты могут вызывать GetOrders, а затем фильтровать по своему усмотрению.

Зачем возиться с репозиторием, если он просто возвращает DBSets?

  1. Централизуйте низкоуровневую фильтрацию данных. Например, если вы используете мягкое удаление (IsActive) или используете мультитенантную или явную авторизацию. Эти общие правила можно централизовать на уровне репозитория, вместо того, чтобы запоминать их везде, где касаются DbSet.
  2. Тестирование проще. Хотя вы можете издеваться над DbContext или указывать его на базу данных в памяти, имитировать репозиторий, возвращающий IQueryable, проще. (Просто заполните List<TEntity> и верните .AsQueryable().
  3. Репозитории обрабатывают создание и удаление. Создайте, чтобы служить фабрикой, чтобы гарантировать, что все необходимые данные и отношения установлены для жизнеспособного объекта. Удалить для обработки сценариев мягкого удаления, каскадов / аудитов и т. д., Помимо того, что БД обрабатывает «за кулисами».

Я пробовал приведенный ниже ответ, который похож на ваш ответ, но мне что-то не хватает. // Эта жестко запрограммированная строка работает policy = db.Policys.Include ("EntityA"). Include ("EntityB"). Where (_ => .ID == ID) .FirstOrDefault (); // Этот код не работает IQueryable <Policy> policy = db.Policys.Where ( => _.ID == ID); foreach (строковый объект в include.Split (',') {policy.Include (entity);} policy = policy.FirstOrDefault (); Вышеупомянутое работает с родительским объектом верхнего уровня, но не с внешними объектами. Они возвращают null .

SeanK 04.09.2018 01:35
foreach (string entity in include.Split(',') { policies = policies.Include(entity); } Вам не хватало назначения обратно в политики.
Steve Py 04.09.2018 02:34

Другие вопросы по теме