как зарегистрировать открытые обобщенные типы с конкретными параметрами в C#

Вопрос или проблема

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

public interface IRepo<T, TKey> where T: class { ... }

public class RepoBase<T, TKey>: IRepo<T, TKey> where T: class {...}
// Внедрение зависимостей
services.AddScoped(typeof(IRepo<,>), typeof(RepoBase<,>));  

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

public interface IRepo<T, TKey> where T: class { ... }

public class RepoBase<T, TKey, D>: IRepo<T, TKey> where T: class {...} where D : DbContext  // D - это DbContext из EF 
{
   ...
}  

public class MyDbContext : DbContext { ... }
services.AddScoped(typeof(IRepo<,>), typeof(RepoBase<,,>));  // компилируется, но я хочу, чтобы третий тип был MyDbContext

services.AddScoped(typeof(IRepo<,>), typeof(RepoBase<,,MyDbContext>)); // не компилируется, но это то, что я хочу

как я могу зарегистрировать открытый обобщенный тип, но предоставить частичный конкретный тип?

Как уже упоминалось в комментариях, вы можете определить такой тип:

public class RepoBaseWithMyContext<T1, T2> : RepoBase<T1, T2, MyDbContext>

а затем зарегистрировать его так:

services.AddScoped(typeof(IRepo<,>), typeof(RepoBaseWithMyContext<,>));

Ответ или решение

Для решения задачи регистрации открытых генерических типов с одним конкретным параметром в C#, мы можем использовать подход, который упрощает и структурирует код, тем самым обеспечивая гибкость в архитектуре приложения. В данной ситуации нам нужно зарегистрировать RepoBase с двумя открытыми типами и одним конкретным типом — MyDbContext, который является производным от DbContext.

Шаги для реализации

  1. Определение интерфейсов и базовых классов:
    Начнем с определения интерфейса IRepo, который будет принимать два типа: T и TKey.

    public interface IRepo<T, TKey> where T : class 
    {
       // Определение методов
    }

    Затем мы создаем базовый класс RepoBase, который реализует этот интерфейс и принимает три типа: T, TKey и D, где D должен быть производным от DbContext.

    public class RepoBase<T, TKey, D> : IRepo<T, TKey> where T : class where D : DbContext
    {
       // Реализация методов
    }
  2. Создание конкретного класса контекста:
    Далее мы определим конкретный класс контекста базы данных, который будет использоваться в нашем репозитории:

    public class MyDbContext : DbContext 
    {
       // Реализация контекста
    }
  3. Создание обертки для регистрации:
    Чтобы зарегистрировать RepoBase с частично конкретным типом, мы создадим вспомогательный класс RepoBaseWithMyContext. Этот класс будет производным от RepoBase и будет передавать MyDbContext в качестве третьего параметра.

    public class RepoBaseWithMyContext<T1, T2> : RepoBase<T1, T2, MyDbContext>
    {
       // Здесь могут быть дополнительные методы или свойства
    }
  4. Регистрация в контейнере зависимостей:
    Теперь, используя AddScoped, мы можем зарегистрировать наш класс в контейнере зависимостей:

    services.AddScoped(typeof(IRepo<,>), typeof(RepoBaseWithMyContext<,>));

Преимущества такого подхода

  • Чистота кода: Создание отдельного класса для обработки конкретного контекста позволяет упростить код и обеспечить его расширяемость.
  • Гибкость: Вы можете легко изменить контекст или добавить новые зависимости, не изменяя основную логику.
  • Поддержка Dependency Injection: Используя механизм DI, вы можете управлять жизненным циклом объектов более эффективно, что приводит к улучшению управляемости приложения.

Заключение

Таким образом, для регистрации открытого генерического типа с частично конкретным типом в C#, мы создаем вспомогательный класс, который наследует базовую логику, тем самым обеспечивая возможность работы с конкретным контекстом базы данных. Этот подход не только предоставляет необходимую гибкость, но и улучшает архитектурное оформление приложения.

Оцените материал
Добавить комментарий

Капча загружается...