Autofac resolve 注册时的服务实现
Autofac resolve service implementation during registration
我正在尝试将代码放在一个地方,以便使用 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}
基本上,用户切换订单 "modes",我将其保留在会话中。
我想做的是使用键控注册解决 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 推荐的(使用服务定位器模式)。但是 - 我觉得将服务解析代码放在我的应用程序的一个位置比使用服务解析组件中的正确类型更干净、更容易维护。
这虽然有效,但看起来很难看。有什么方法可以使用 Keyed 服务解决方案吗?换句话说,注册这两个服务并让 autofac 根据已解析的 ProfileService.OrderingMode?
解析 ResolveKeyed(OrderType) 的正确服务实现
我或多或少试图在这里验证我的方法。如果有人有更好的方法,我将不胜感激。
更新
我正在尝试使用
IIndex<T,V>
我觉得我很接近 - 但我得到一个服务未注册的异常。
_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>();
我不知道为什么这行不通。
明白了。
原来我错了两件事。
我从来没有向要解析的接口注册服务。
_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 项服务将变得 "context" 依赖于用户的行为。这使得管理向用户提供什么服务的解决方案变得清晰和可维护。
根据用户所处的模式,所有解决的服务都是正确的,我不需要在控制器中检查它。
我希望其他人可以找到它的用处。
我正在尝试将代码放在一个地方,以便使用 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}
基本上,用户切换订单 "modes",我将其保留在会话中。
我想做的是使用键控注册解决 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 推荐的(使用服务定位器模式)。但是 - 我觉得将服务解析代码放在我的应用程序的一个位置比使用服务解析组件中的正确类型更干净、更容易维护。
这虽然有效,但看起来很难看。有什么方法可以使用 Keyed 服务解决方案吗?换句话说,注册这两个服务并让 autofac 根据已解析的 ProfileService.OrderingMode?
解析 ResolveKeyed(OrderType) 的正确服务实现
我或多或少试图在这里验证我的方法。如果有人有更好的方法,我将不胜感激。
更新
我正在尝试使用
IIndex<T,V>
我觉得我很接近 - 但我得到一个服务未注册的异常。
_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>();
我不知道为什么这行不通。
明白了。
原来我错了两件事。
我从来没有向要解析的接口注册服务。
_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 项服务将变得 "context" 依赖于用户的行为。这使得管理向用户提供什么服务的解决方案变得清晰和可维护。
根据用户所处的模式,所有解决的服务都是正确的,我不需要在控制器中检查它。
我希望其他人可以找到它的用处。