Autofac разрешить внедрение сервиса при регистрации
Я пытаюсь поместить код в одном месте для разрешения служб Keyed с помощью Autofac.
У меня есть интерфейс:
public interface IShipManagerService { }
Две реализации:
public class InventoryShipManagerService : IShipManagerService
{
}
public class DemandShipManagerService : IShipManagerService
{
}
У меня также есть UserProfileService, в котором содержится некоторая информация о сеансе:
public interface IUserProfileService
{
OrderType OrderingMode {get;set;}
}
public enum OrderType {Inventory, Demand}
Обычно пользователь переключает "режимы" упорядочивания, и я сохраняю это в сессии.
То, что я пытаюсь сделать, это решить правильную реализацию IShipManagerService с помощью регистрации по ключу - но я хочу поставить его в одном месте.
У меня есть это:
builder.RegisterType<InventoryShipManagerService>()
.As<IShipManagerService>().Keyed<OrderType>(OrderType.Inventory);
builder.RegisterType<InventoryShipManagerService>()
.As<IShipManagerService>().Keyed<OrderType>(OrderType.Inventory);
Разрешение будет с использованием рекомендованного Autofac IIndex
private readonly IShipManagerService _shipManagerService;
private readlony IUserProfileService _profileService;
public class ShipToController
{
public ShipToController(IUserProfileService profileService, IIndex<OrderType, IShipManagerService> shipManagerList)
{
_profileService = profileService;
_shipManagerService = shipManagerList[_profileService.OrderType];
}
Это будет работать - но я не хочу помещать это везде, где я использую IShipManagerService (есть другие сервисы, которые попадают в эту категорию) - мои конструкторы контроллеров очень быстро запутаются.
То, что я хочу сделать, это что-то вроде этого (у меня это работает)
builder.Register(ctx =>
{
//so I can get the current "Ordering Mode"
var profileService = ctx.Resolve<IUserProfileService>();
//Default to Inventory
IShipManagerService service = (Inventory)Activator.CreateInstance(typeof(InventoryShipManagerService),
ctx.Resolve<IRepository>(),
ctx.Resolve<IShoppingCartService>()) as IShipManagerService;
switch (profileService.OrderingMode)
{
case OrderingMode.Demand:
//if demand is "on"
service = (DemandShipManagerService)Activator.CreateInstance(typeof(DemandShipManagerService),
ctx.Resolve<IRepository>(),
ctx.Resolve<IShoppingCartService>()) as IShipmanagerService;
}
return service;
}
Две вещи здесь.
Это работает, и я знаю, что это не то, что рекомендует Autofac (используя шаблон Service Locator). Однако - я считаю, что проще и проще поддерживать код разрешения службы в одном месте моего приложения, а не определять правильный тип в компонентах, использующих службы.
Это - пока это работает - кажется уродливым. Есть ли способ использовать разрешение службы Keyed? Другими словами, зарегистрируйте обе службы и разрешите автофаку разрешить правильную реализацию службы ResolveKeyed(OrderType) на основе разрешенного ProfileService.OrderingMode?
Я более или менее пытаюсь подтвердить свой подход здесь. Буду признателен за лучший способ, если у кого-то есть.
ОБНОВИТЬ
Я пытаюсь использовать
IIndex<T,V>
который рекомендует Autofac.
Я чувствую себя так близко - но я получаю услугу незарегистрированным исключением.
_builder.RegisterType<TShelfShipToManagerService>().Keyed<IShipToManagerService>(OrderType.Shelf);
_builder.RegisterType<TDemandShipToManagerService>().Keyed<IShipToManagerService>(OrderType.Demand);
_builder.Register(ctx =>
{
var profileService = ctx.Resolve<IUserProfileService>();
//The way Autofac recomends
var services = ctx.Resolve<IIndex<OrderType, IShipToManagerService>>();
//I get Component not Registered here??????
return services[profileService.OrderingType];
//this will go away if I can get the code above to work
IShipToManagerService service = Activator.CreateInstance(typeof(TShelfShipToManagerService),
ctx.Resolve<IRepository>(),
ctx.Resolve<IIntegrationService>(),
ctx.Resolve<IShoppingCartService>(),
ctx.Resolve<IUserProfileService>()
) as IShipToManagerService;
switch (profileService.OrderingType)
{
case OrderType.Demand:
service = Activator.CreateInstance(typeof(TDemandShipToManagerService),
ctx.Resolve<IRepository>(),
ctx.Resolve<IIntegrationService>(),
ctx.Resolve<IShoppingCartService>(),
ctx.Resolve<IUserProfileService>()) as IShipToManagerService;
break;
}
return service;
}).As<IShipToManagerService>();
Я не знаю, почему это не будет работать.
1 ответ
Понял.
Оказывается, у меня было две вещи неправильно.
Я никогда не регистрировал сервисы с интерфейсом для разрешения.
_builder.RegisterType<TShelfShipToManagerService>() .Keyed<IShipToManagerService>(OrderType.Shelf) .As<IShipToManagerService>(); //was missing that part _builder.RegisterType<TDemandShipToManagerService>() .Keyed<IShipToManagerService>(OrderType.Demand) .As<IShipToManagerService>(); //was missing that part
Подход предполагал, что Режим заказа будет доступен из сервиса UserProfile. У меня было достаточно обработки исключений, чтобы убедиться, что это служба UserProfile по умолчанию (для запуска и до входа пользователя в систему - ничего не происходит). Проблема заключалась в том, что я пытался разрешить использование службы с ключами, у которой был режим упорядочения = 0 (так как режим упорядочения является перечислением, и он не был установлен сеансом пользователя, он устанавливается в ноль).
Сверху я зарегистрировал только два ключевых сервиса OrderType.Shelf и OrderType.Demand - так когда
_builder.Register(ctx =>
{
var profileService = ctx.Resolve<IUserProfileService>();
//The way Autofac recomends
var services = ctx.Resolve<IIndex<OrderType, IShipToManagerService>>();
//The first request ends up with an OrderingType == 0
//Since I haven established the session
//I don't have service registered with a key 0
return services[profileService.OrderingType];
}).As<IShipToManagerService>();
Таким образом, исправление состояло в том, чтобы просто проверить OrderingType == 0 - если это так - тогда используйте настройку по умолчанию.
Здесь у меня есть AppSettingService, который предоставляет глобальный параметр DefaultOrderingMode.
_builder.Register(ctx =>
{ var profileService = ctx.Resolve<IUserProfileService>();
//Check to see if their is a user profile (OrderType = 0)
//if not - then get the default setting
if (profileService.OrderingType == 0)
{
var appSettingService = ctx.Resolve<IApplicationSettingService>();
profileService.OrderingType = appSettingService.GetApplicationSetting(ApplicationSettings.DefaultOrderingMode).ToEnumTypeOf<OrderType>();
}
//The way Autofac recomends
var services = ctx.Resolve<IIndex<OrderType, IShipToManagerService>>();
return services[profileService.OrderingType];
}).As<IShipToManagerService>();
Это то, чего я пытался достичь.
У меня есть около 10 сервисов, которые станут зависимыми от контекста в зависимости от того, что делает пользователь. Это делает управление разрешением того, какой сервис предоставляется пользователю, чистым и обслуживаемым.
Все разрешенные службы будут корректными в зависимости от того, в каком режиме находится пользователь, и мне не нужно проверять его в контроллерах.
Я надеюсь, что кто-то еще может найти применение этому.