Я хочу внедрить этот интерфейс в свои контроллеры:
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;
}
}
Если получится? И является ли хорошей практикой создание универсального интерфейса для внедрения в контроллеры? Если нет, то как это лучше сделать?
Другой вопрос, есть ли смысл в моем мышлении? Какова наилучшая практика в сценарии, который я объяснил в своем вопросе?
И достаточно ли внедрить классы вроде ProjectService
в мои контроллеры?
В чем польза ProjectService
и IDatabaseService
, когда они лишь тонким слоем оборачивают ваши DbContext
? Используйте свой DbContext
напрямую и используйте его In-Memory провайдера для тестовых сценариев.
Я хотел работать только над DbSet
, думая о Projects
, так как же это работает в сценариях «реальной жизни»? Лучше иметь дело с одним IDatabaseService
и DatabaseService
если с каждым по одному контроллеру? Я думаю, что ProjectController
не нужно иметь (например) ссылку на Tasks
Вы можете сделать это, добавив строку ниже в Startup.cs
// best practice
services.AddTransient(typeof(IDatabaseService<>),typeof(DatabaseService<>));
Посетите Здесь, чтобы узнать больше о Внедрение зависимостей в ASP.NET Core
services.AddTransient<IDatabaseService<T>, DatabaseService<T>>();
это не будет работать с универсальным типом, вам придется добавить конкретный тип вместо T. При использовании универсальных параметров они должны быть известны во время компиляции. Вторая строка — это единственный способ зарегистрировать ее, не зная и не определяя конкретный тип (типы) (для каждой регистрации типа)
Я хотел работать только над DbSet
, думая о Projects
, так как же это работает в сценариях «реальной жизни»? Лучше иметь дело с одним IDatabaseService
и DatabaseService
если с каждым по одному контроллеру? Я думаю, что ProjectController
не нужно иметь (например) ссылку на Tasks
Спасибо за комментарии. Я удалил неправильный пример.
Спасибо, но, как вы можете видеть в моем вопросе, я хочу иметь такие классы, как TaskService : IDatabaseService<Task>
и ProjectService : IDatabaseService<Project>
. У меня нет класса "Служба базы данных"
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);
});
Не могли бы вы дать более сложное объяснение по поводу второго абзаца?
Если у вас есть большое количество сервисов, реализующих интерфейс IDatabaseService<>, он позволяет найти и зарегистрировать их все.
Вы можете использовать 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<,>));
Полный исходный код можно прочитать в моем блоге ngohungphuc.wordpress.com/2018/05/01/….
Не стесняйтесь использовать помощников:
по универсальному интерфейсу
services.AddAllGenericTypes(typeof(IDatabaseService<>), new[] {typeof(ProjectService).GetTypeInfo().Assembly});
С расширениями от: https://gist.github.com/GetoXs/5caf0d8cfe6faa8a855c3ccef7c5a541
Скратор может это сделать? github.com/хелланг/Scrutor
Вы должны быть в состоянии сделать
services.AddScoped(typeof(IDatabaseService<>));