Универсальная служба EntityFramework с универсальными включениями

У меня есть общий сервис, который определяется следующим образом:

public interface IGenericService<T> : IDisposable
where T : class, IModel
{
    Task<bool> Add(T entity);

    Task<bool> AddOrUpdate(T entity);

    Task<bool> Update(T entity);

    Task<T?> Get(int id);

    Task<bool> Exists(int id);

    Task<IEnumerable<T>> GetAll();

    Task<bool> Delete(T entity);

    Task<IEnumerable<T>> GetAll(int numberOfElements, int page);

    Task<int> NumberOfEntries();

    Task<T?> Refresh(T entity);
}

который реализован в GenericService.cs. Это работает очень хорошо, даже с дочерними классами, например. "MyService.cs". В настоящее время я очищаю код и обнаружил одну проблему, которая меня не устраивает.

Если MyService с типом шаблона MyClass имеет отношение к MyOtherClass и я хочу использовать свой общий метод Get-Method, я не получаю объект MyOtherClass. Поэтому мне нужно создать новый Get-Method в MyService с соответствующим include, который выглядит примерно так:

public new async Task<MyClass?> Get(int id)
{
    try
    {
        return await this.Context.Set<MyClass>().Include(c => c.MyOtherClass).FirstOrDefaultAsync(d => d.Id == id);
    }
    catch (Exception)
    {
        return null;
    }
}

Это не проблема сама по себе, но я начал выяснять способ общей загрузки включений для всех дочерних классов. Это означает, что у меня есть только одна реализация Get-Method, а не отдельная реализация почти для всех моих сервисов.

Моя идея заключалась в том, что в GenericService я добавляю абстрактный метод:

protected abstract IEnumerable<string> GetIncludes();

который затем вызывается в Get-методе GenericService:

public async Task<TModel?> Get(int id)
{
    try
    {
        var query = this.Context.Set<TModel>();
        foreach (var include in this.GetIncludes())
        {
            query.Include(include);
        }

        return await query.FindAsync(id);
    }
    catch (Exception)
    {
        return null;
    }
}

Затем в моих дочерних классах я просто реализую абстрактный метод следующим образом:

protected override IEnumerable<string> GetIncludes()
{
    return new List<string>() { "MyOtherClass" };
}

Проблема в том, что этот подход не работает. Если я отлаживаю Get-Method моего GenericService, я получаю правильный список включений, и цикл for выполняется правильно, но MyOtherClass всегда имеет значение null. (Очевидно, что это работает, когда я переопределяю метод Get и напрямую пишу this.Context.Set<MyClass>().Include(c => c.MyOtherClass))

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

Ответы 1

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

Вы забыли назначить измененный запрос:

query = query.Include(include);
public async Task<TModel?> Get(int id)
{
    try
    {
        var query = this.Context.Set<TModel>().AsQueryable();
        foreach (var include in this.GetIncludes())
        {
            query = query.Include(include);
        }

        return await query.FirstOrDefaultAsync(m => m.Id == id);
    }
    catch (Exception)
    {
        return null;
    }
}

Я понимаю! Без AsQueryable() не работает все вместе. Сделав его доступным для запросов и назначив измененный запрос, он работает. Спасибо!

Zumpel 07.04.2022 13:39

Мне просто пришлось изменить оператор возврата на: return await query.Where(m => m.Id == id).FirstOrDefaultAsync();, потому что FindAsync в этом случае не работает.

Zumpel 07.04.2022 13:40

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

ASP NET MVC 6 — как получить количество продуктов для каждого значения перечисления
EntityFramework (только аннотации данных) Связь не разрешается с идентификатором
Исправить «Данные имеют значение Null. Этот метод или свойство нельзя вызывать для нулевых значений» без использования аннотаций данных,
Разделить одну строку на несколько на основе разделения строки в нескольких ячейках
Каков наилучший способ присоединиться к нескольким таблицам с помощью Entity framework?
Внешний ключ без идентификаторов для столбца с тем же именем, что и свойство навигации
Как остановить добавление миграции dotnet ef от создания ненужных дополнительных полей
System.InvalidOperationException: «Не удалось перевести выражение LINQ. Либо переписать запрос в форме, которую можно перевести
Как использовать строку подключения EF и строку подключения ADO.Net в одном проекте
Как использовать строку при выборе в Entity Framework на .NET 6