Внедрить универсальный интерфейс в .NET Core

Я хочу внедрить этот интерфейс в свои контроллеры:

public interface IDatabaseService<T>
{
    IEnumerable<T> GetList();
    ...
}

Я хочу использовать универсальный, потому что в моем проекте WebApi у меня есть такие контроллеры, как ProjectController, TaskController и т. д., и я хочу использовать универсальный интерфейс для каждого типа (например, IDatabaseService<Project>, IdatabaseService<Task> и т. д.).

Класс, который будет внедрен в контроллер, будет выглядеть так:

public class ProjectService : IDatabaseService<Project>
{
    private readonly DbContext context;

    public ProjectService(DbContext context)
    {
        this.context = context;
    }

    public IEnumerable<Project> GetList() { }
    ...
}

Но когда я пытаюсь ввести инъекцию в свой Startup.cs:

services.AddScoped<IDatabaseService<T>>();

Мне нужно пройти тип T.

Мой вопрос: как сделать инъекцию универсальной и как правильно ввести ее в контроллер? Например:

public class ProjectController : ControllerBase
{
    private readonly ProjectService projectService;

    public ProjectController (IDatabaseService<Project> projectService)
    {
        this.projectService = projectService;
    }
}

Если получится? И является ли хорошей практикой создание универсального интерфейса для внедрения в контроллеры? Если нет, то как это лучше сделать?

Вы должны быть в состоянии сделать services.AddScoped(typeof(IDatabaseService<>));

Izzy 15.05.2019 09:23

Другой вопрос, есть ли смысл в моем мышлении? Какова наилучшая практика в сценарии, который я объяснил в своем вопросе?

michasaucer 15.05.2019 09:23

И достаточно ли внедрить классы вроде ProjectService в мои контроллеры?

michasaucer 15.05.2019 09:26

В чем польза ProjectService и IDatabaseService, когда они лишь тонким слоем оборачивают ваши DbContext? Используйте свой DbContext напрямую и используйте его In-Memory провайдера для тестовых сценариев.

haim770 15.05.2019 09:27

Я хотел работать только над DbSet, думая о Projects, так как же это работает в сценариях «реальной жизни»? Лучше иметь дело с одним IDatabaseService и DatabaseService если с каждым по одному контроллеру? Я думаю, что ProjectController не нужно иметь (например) ссылку на Tasks

michasaucer 15.05.2019 09:32
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
17
5
28 691
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете сделать это, добавив строку ниже в Startup.cs

// best practice  
services.AddTransient(typeof(IDatabaseService<>),typeof(DatabaseService<>));

Посетите Здесь, чтобы узнать больше о Внедрение зависимостей в ASP.NET Core

services.AddTransient<IDatabaseService<T>, DatabaseService<T>>(); это не будет работать с универсальным типом, вам придется добавить конкретный тип вместо T. При использовании универсальных параметров они должны быть известны во время компиляции. Вторая строка — это единственный способ зарегистрировать ее, не зная и не определяя конкретный тип (типы) (для каждой регистрации типа)
Tseng 15.05.2019 09:32

Я хотел работать только над DbSet, думая о Projects, так как же это работает в сценариях «реальной жизни»? Лучше иметь дело с одним IDatabaseService и DatabaseService если с каждым по одному контроллеру? Я думаю, что ProjectController не нужно иметь (например) ссылку на Tasks

michasaucer 15.05.2019 09:34

Спасибо за комментарии. Я удалил неправильный пример.

Voodoo 15.05.2019 09:35

Спасибо, но, как вы можете видеть в моем вопросе, я хочу иметь такие классы, как TaskService : IDatabaseService<Task> и ProjectService : IDatabaseService<Project>. У меня нет класса "Служба базы данных"

michasaucer 15.05.2019 09:38
Ответ принят как подходящий

1.) если вы хотите написать жесткий код

services.AddScoped<IDatabaseService<Project>, ProjectService>();

2.) если вы хотите прописать динамически то все типы реализованы IDatabaseService<>

        System.Reflection.Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(item => item.GetInterfaces()
            .Where(i => i.IsGenericType).Any(i => i.GetGenericTypeDefinition() == typeof(IDatabaseService<>)) && !item.IsAbstract && !item.IsInterface)
            .ToList()
            .ForEach(assignedTypes =>
            {
                var serviceType = assignedTypes.GetInterfaces().First(i => i.GetGenericTypeDefinition() == typeof(IDatabaseService<>));
                services.AddScoped(serviceType, assignedTypes);
            });

Не могли бы вы дать более сложное объяснение по поводу второго абзаца?

michasaucer 15.05.2019 10:14

Если у вас есть большое количество сервисов, реализующих интерфейс IDatabaseService<>, он позволяет найти и зарегистрировать их все.

levent 15.05.2019 10:22

Вы можете использовать services.AddScoped, чтобы использовать только 1 экземпляр в запросе области действия. Так что в целом улучшение по сравнению с AddTransient

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));

Итак, мой интерфейс и класс будут выглядеть так

public interface IGenericRepository<T> where T : class
public class GenericRepository<T> : IGenericRepository<T> where T : class

Например, этот вариант использования, как мы справляемся? открытый класс GenericRepository<T, U> : IGenericRepository<T> где T : класс, в котором класс U я пробовал ниже кода, но службы ошибок времени выполнения. AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<,>));

mehmetgelmedi 15.05.2020 16:53

Полный исходный код можно прочитать в моем блоге ngohungphuc.wordpress.com/2018/05/01/….

Tony Ngo 16.05.2020 02:55

Не стесняйтесь использовать помощников:

по универсальному интерфейсу

services.AddAllGenericTypes(typeof(IDatabaseService<>), new[] {typeof(ProjectService).GetTypeInfo().Assembly});

С расширениями от: https://gist.github.com/GetoXs/5caf0d8cfe6faa8a855c3ccef7c5a541

Скратор может это сделать? github.com/хелланг/Scrutor

Victorio Berra 14.01.2021 17:59

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