Внедрение зависимостей в сервисе и для веб-приложения
Я использую внедрение зависимостей для своего бизнес-уровня, который содержит сервисы, такие как мой пример ниже:
public class MyService : IMyService
{
private IMyDbContext DbContext;
public MyService(IMyDbContext dbContext)
{
this.DbContext = dbContext;
}
public DoSomething(int id)
{
// Use the business layer for something
var user = this.DbContext.Set<User>().Find(id);
}
}
Как видите, мой сервис использует мою Entity Framework DbContext в качестве зависимости. Я использую Ninject в качестве своего контейнера IoC, но я думаю, что это может относиться к любому.
Конфигурация веб-сайта ASP.NET MVC
Для моего веб-сайта ASP.NET это настроено с помощью Ninject для создания единого IMyDbContext
за запрос и только один экземпляр IMyService
требуется для запроса - так что это приятно и просто.
- IMyService - Переходный процесс
- IMyDbContext - По запросу
Конфигурация службы Windows
Я хочу настроить его так, чтобы на каждой итерации моего Window Service мне не приходилось "обновлять" мой сервис для каждой итерации.
Я хочу иметь следующую конфигурацию:
- IMyService - один экземпляр или переходный процесс
- IMyDbContext - новый экземпляр каждый раз, когда вызывается метод
Эта проблема
Как этого достичь, когда я делюсь своими услугами между двумя разными проектами.
Мой друг предложил использовать Фабрику для моего IDbContext
что было бы хорошо, если бы я не хотел продолжать использовать Per Request Scope
в моем приложении ASP.NET - но я делаю. Я не уверен, что могу настроить фабрику так, чтобы она возвращала один и тот же экземпляр каждый раз только для этой цели и новый экземпляр для каждого запроса в моей службе Windows.
Проблема с этим, например, если я использую фабрику сейчас (для службы Windows, которая требует новый экземпляр для каждого вызова DoSomething()
):
public class MyService : IMyService
{
private IMyContextFactory DbContextFactory;
public MyService(IMyContextFactory dbContextFactory)
{
this.DbContextFactory = dbContextFactory;
}
public DoSomething(int id)
{
// Get instance of the db for use
var dbContext = this.DbContextFactory.GetInstance();
// Use the business layer for something
var user = dbContext.Set<User>().Find(id);
}
}
Как вы можете видеть, мне пришлось добавить строку кода, чтобы использовать фабрику.
Это больше не работает для моего веб-сайта, который хочет использовать один (на запрос) экземпляр DbContext.
Мне нужна конфигурация, которая будет работать для обоих сценариев, так как я делюсь базой кода.
У меня уже есть Boostrapper для обоих проектов, который настраивает kernel
Привязки, это просто, как я настраиваю свои привязки для различных сценариев, разделяя базу кода.
2 ответа
поскольку MVC и служба Windows - это два разных хоста с разными требованиями к времени жизни внедренных зависимостей, они должны иметь свои собственные регистрации Ninject. MVC будет определять IMyService иначе, чем Windows Service Host.
если это невозможно и вам нужно использовать один и тот же регистрационный код для обоих хостов, тогда тип приложения может быть именованным распознавателем для создания экземпляра IMyService.
kernel.Bind<IMyService>().To<MyService>().Named("MVC"); // add lifetime thingy kernel.Bind<IMyService>().To<MyService>().Named("WindowsService"); // add lifetime // usage var iammvc= kernel.Get<IMyService>("MVC");
если Ninject получает "MVC", вы можете настроить его 1 способом, а если он получает "WindowsService", вы можете настроить его по-другому.
Вариант 1 обычно предпочтительнее, поскольку MVC и Windows Service имеют разные контексты выполнения (Http Request n all), и легче контролировать их зависимости независимо.
Мне сказали, что расширение фабрики использует IResolutionRoot
разрешить зависимости.
Это означает, что когда фабрика создает экземпляр, он создает его в соответствии с существующей конфигурацией типа, как в привязках.
Если я установлю следующую привязку:
Kernel.Bind<IMyDbContext>().To<MyDbContext>().InRequestScope();
Когда я вызываю фабрику, она возвращает один и тот же экземпляр для каждого звонка фабрике:
// All of these will return the same DbContext
var dbContext1 = this.DbContextFactory.Create();
var dbContext2 = this.DbContextFactory.Create();
var dbContext3 = this.DbContextFactory.Create();
Принимая во внимание, что для моего Window Service я хотел, чтобы это был новый экземпляр каждый раз, я установил DbContext на переходный процесс:
Kernel.Bind<IMyDbContext>().To<MyDbContext>().InTransientScope();
Тогда я могу позвонить на завод в рамках моего сервиса:
// This will return a new instance of my DbContext every time
var dbContext1 = this.DbContextFactory.Create();
var dbContext2 = this.DbContextFactory.Create();
var dbContext3 = this.DbContextFactory.Create();
Вот привязка для фабрики:
Kernel.Bind<IMyDbContextFactory>().ToFactory();
Вот интерфейс для Фабрики:
public interface IMyDbContextFactory
{
IMyDbContext Create();
}