根据运行时会话值从 Autofac 解析服务实现
Resolve Service Implementation from Autofac based on Runtime Session Value
需要一些帮助来尝试解决基于参数在运行时解决服务实现的问题。换句话说,使用带有 DI 的工厂模式。
我们已将 Autofac 连接到我们的 MVC 应用程序。我试图弄清楚我们如何使用用户会话变量(称为订购类型)用于依赖项解析器来解析服务的正确实现。
我们正在尝试做的事情的一个例子。
该应用程序有两个 "types" 订单 - 真正的电子商务类型的订单(将东西添加到购物车、结帐等)。
另一个叫做预测排序。用户创建订单 - 但他们不会立即得到履行。他们通过审批程序然后完成。
最重要的是数据模式和后端系统,应用程序会根据订单类型进行更改。
我想做的是:
我有 IOrderManagerService
public interface IOrderManagerService
{
Order GetOrder(int orderNumber);
int CreateOrder(Order order);
}
因为我们有两个排序 "types" - 我有两个 IOrderManagerService 的实现:
public class ShelfOrderManager : IOrderManagerService
{
public Order GetOrder(int orderMumber)
{
...code
}
public int CreateOrder(Order order)
{
...code
}
}
和
public class ForecastOrderManager: IOrderManagerService
{
public Order GetOrder(int orderMumber)
{
...code
}
public int CreateOrder(Order order)
{
...code
}
}
我的第一个问题是 - 在我的 MVC 应用程序中 - 我是否将这些实现注册为?
builder.RegisterType<ShelfOrderManager>().As<IOrderManagerService>();
builder.RegisterType<ForecastOrderManager>().As<IOrderManagerService>();
我们计划做的是将用户选择的订购类型固定在用户会话中。当用户想要查看订单状态时 - 根据他们选择的订单 "type" - 我需要解析器为控制器提供正确的实现。
public class OrderStatusController : Controller
{
private readonly IOrderManagerService _orderManagerService;
public OrderStatusController(IOrderManagerService orderManagerService)
{
//This needs to be the correct implementation based on the users "type".
_orderManagerService = orderManagerService;
}
public ActionResult GetOrder(int orderNumber)
{
var model = _orderManagerService.GetOrder(orderNumber);
return View(model);
}
}
我已经准备好 the delegate factory and this answer 很好地解释了这个概念。
问题是运行时参数被用于构建服务并在运行时解析。即
var service = resolvedServiceClass.Factory("runtime parameter")
所有这一切都会给我"service",它在构造函数中使用了"runtime parameter"。
我也看过 Keyed or Named 分辨率。
起初我以为我可以结合这两种技术 - 但控制器依赖于接口 - 而不是具体实现。 (应该如此)
任何有关如何解决此问题的想法都将受到 MUCH 的赞赏。
我不会为此依赖 Autofac。 IOC 用于解决依赖关系并为其提供实现,您需要的是根据决策标志调用同一接口的不同实现。
我基本上会使用一个简单的工厂,例如带有 2 个静态方法的 class,并在您知道决定是什么时调用您需要的任何实现。这为您提供了您所追求的 运行 时间解析器。我会说保持简单。
话虽这么说,但似乎还有另一种选择。查看 "select by context" 选项,也许您可以重新设计 classes 以利用此选项:http://docs.autofac.org/en/latest/faq/select-by-context.html
事实证明我们很接近。 @Andrei 与我们所做的目标一致。我将在下面为遇到此问题的下一个人解释答案。
重述问题 - 我需要在 运行 时使用 Autofac 解决接口的特定具体实现。这通常由工厂模式解决 - 但我们已经实现了 DI。
解决方案是同时使用两者。使用 delegate factory Autofac 支持,我创建了一个简单的工厂 class。
我选择私下解析组件上下文
DependencyResolver.Current.GetService<IComponentContext>();
与让 Autofac 主要解析它相比,我不必在将使用工厂的所有构造函数中包含 IComponentContext。
工厂将用于解析依赖于 运行 时间参数的服务 - 这意味着
ISomeServiceThatHasMultipleImplementations
在构造函数中使用 - 我将用 ServiceFactory.Factory 工厂替换它。我不想在需要工厂的任何地方也包含 IComponentContext。
enum OrderType
{
Shelf,
Forecast
}
public class ServiceFactory : IServiceFactory
{
private readonly IComponentContext _componentContext;
private readonly OrderType _orderType;
public ServiceFactory(OrderType orderingType)
{
_componentContext = DependencyResolver.Current.GetService<IComponentContext>();
_orderType = orderingType;
}
public delegate ServiceFactory Factory(OrderType orderingType);
public T Resolve<T>()
{
if(!_componentContext.IsRegistered<T>())
return _componentContext.ResolveNamed<T>(_orderType.ToString());
return _componentContext.Resolve<T>();
}
}
随着工厂的编写,我们还使用了Keyed服务。
使用我的订单上下文 -
public interface IOrderManagerService
{
Order GetOrder(int orderNumber);
int CreateOrder(Order order);
}
public class ShelfOrderManager : IOrderManagerService
{
public Order GetOrder(int orderNumber)
{
...
}
public int CreateOrder(Order order)
{
...
}
}
public class ForecastOrderManager : IOrderManagerService
{
public Order GetOrder(int orderNumber)
{
...
}
public int CreateOrder(Order order)
{
...
}
}
Keyed服务注册:
//register the shelf implementation
builder.RegisterType<ShelfOrderManager>()
.Keyed(OrderType.Shelf)
.As<IOrderManager>();
//register the forecast implementation
builder.RegisterType<ForecastOrderManager>()
.Keyed(OrderType.Shelf)
.As<IOrderManager>();
注册工厂:
builder.RegisterType<IMS.POS.Services.Factory.ServiceFactory>()
.AsSelf()
.SingleInstance();
最终在控制器中使用它(或任何其他 class):
public class HomeController : BaseController
{
private readonly IContentManagerService _contentManagerService;
private readonly IViewModelService _viewModelService;
private readonly IApplicationSettingService _applicationSettingService;
private readonly IOrderManagerService _orderManagerService;
private readonly IServiceFactory _factory;
public HomeController(ServiceFactory.Factory factory,
IViewModelService viewModelService,
IContentManagerService contentManagerService,
IApplicationSettingService applicationSettingService)
{
//first assign the factory
//We keep the users Ordering Type in session - if the value is not set - default to Shelf ordering
_factory = factory(UIUserSession?.OrderingMode ?? OrderType.Shelf);
//now that I have a factory to get the implementation I need
_orderManagerService = _factory.Resolve<IOrderManagerService>();
//The rest of these are resolved by Autofac
_contentManagerService = contentManagerService;
_viewModelService = viewModelService;
_applicationSettingService = applicationSettingService;
}
}
我想对 Resolve 方法进行更多处理 - 但对于第一遍,这是有效的。有点工厂模式(在我们需要的地方),但仍然使用 Autofac 来完成大部分工作。
需要一些帮助来尝试解决基于参数在运行时解决服务实现的问题。换句话说,使用带有 DI 的工厂模式。
我们已将 Autofac 连接到我们的 MVC 应用程序。我试图弄清楚我们如何使用用户会话变量(称为订购类型)用于依赖项解析器来解析服务的正确实现。
我们正在尝试做的事情的一个例子。
该应用程序有两个 "types" 订单 - 真正的电子商务类型的订单(将东西添加到购物车、结帐等)。
另一个叫做预测排序。用户创建订单 - 但他们不会立即得到履行。他们通过审批程序然后完成。
最重要的是数据模式和后端系统,应用程序会根据订单类型进行更改。
我想做的是:
我有 IOrderManagerService
public interface IOrderManagerService { Order GetOrder(int orderNumber); int CreateOrder(Order order); }
因为我们有两个排序 "types" - 我有两个 IOrderManagerService 的实现:
public class ShelfOrderManager : IOrderManagerService { public Order GetOrder(int orderMumber) { ...code } public int CreateOrder(Order order) { ...code } }
和
public class ForecastOrderManager: IOrderManagerService
{
public Order GetOrder(int orderMumber)
{
...code
}
public int CreateOrder(Order order)
{
...code
}
}
我的第一个问题是 - 在我的 MVC 应用程序中 - 我是否将这些实现注册为?
builder.RegisterType<ShelfOrderManager>().As<IOrderManagerService>(); builder.RegisterType<ForecastOrderManager>().As<IOrderManagerService>();
我们计划做的是将用户选择的订购类型固定在用户会话中。当用户想要查看订单状态时 - 根据他们选择的订单 "type" - 我需要解析器为控制器提供正确的实现。
public class OrderStatusController : Controller { private readonly IOrderManagerService _orderManagerService; public OrderStatusController(IOrderManagerService orderManagerService) { //This needs to be the correct implementation based on the users "type". _orderManagerService = orderManagerService; } public ActionResult GetOrder(int orderNumber) { var model = _orderManagerService.GetOrder(orderNumber); return View(model); } }
我已经准备好 the delegate factory and this answer 很好地解释了这个概念。
问题是运行时参数被用于构建服务并在运行时解析。即
var service = resolvedServiceClass.Factory("runtime parameter")
所有这一切都会给我"service",它在构造函数中使用了"runtime parameter"。
我也看过 Keyed or Named 分辨率。
起初我以为我可以结合这两种技术 - 但控制器依赖于接口 - 而不是具体实现。 (应该如此)
任何有关如何解决此问题的想法都将受到 MUCH 的赞赏。
我不会为此依赖 Autofac。 IOC 用于解决依赖关系并为其提供实现,您需要的是根据决策标志调用同一接口的不同实现。
我基本上会使用一个简单的工厂,例如带有 2 个静态方法的 class,并在您知道决定是什么时调用您需要的任何实现。这为您提供了您所追求的 运行 时间解析器。我会说保持简单。
话虽这么说,但似乎还有另一种选择。查看 "select by context" 选项,也许您可以重新设计 classes 以利用此选项:http://docs.autofac.org/en/latest/faq/select-by-context.html
事实证明我们很接近。 @Andrei 与我们所做的目标一致。我将在下面为遇到此问题的下一个人解释答案。
重述问题 - 我需要在 运行 时使用 Autofac 解决接口的特定具体实现。这通常由工厂模式解决 - 但我们已经实现了 DI。
解决方案是同时使用两者。使用 delegate factory Autofac 支持,我创建了一个简单的工厂 class。
我选择私下解析组件上下文
DependencyResolver.Current.GetService<IComponentContext>();
与让 Autofac 主要解析它相比,我不必在将使用工厂的所有构造函数中包含 IComponentContext。
工厂将用于解析依赖于 运行 时间参数的服务 - 这意味着
ISomeServiceThatHasMultipleImplementations
在构造函数中使用 - 我将用 ServiceFactory.Factory 工厂替换它。我不想在需要工厂的任何地方也包含 IComponentContext。
enum OrderType
{
Shelf,
Forecast
}
public class ServiceFactory : IServiceFactory
{
private readonly IComponentContext _componentContext;
private readonly OrderType _orderType;
public ServiceFactory(OrderType orderingType)
{
_componentContext = DependencyResolver.Current.GetService<IComponentContext>();
_orderType = orderingType;
}
public delegate ServiceFactory Factory(OrderType orderingType);
public T Resolve<T>()
{
if(!_componentContext.IsRegistered<T>())
return _componentContext.ResolveNamed<T>(_orderType.ToString());
return _componentContext.Resolve<T>();
}
}
随着工厂的编写,我们还使用了Keyed服务。
使用我的订单上下文 -
public interface IOrderManagerService
{
Order GetOrder(int orderNumber);
int CreateOrder(Order order);
}
public class ShelfOrderManager : IOrderManagerService
{
public Order GetOrder(int orderNumber)
{
...
}
public int CreateOrder(Order order)
{
...
}
}
public class ForecastOrderManager : IOrderManagerService
{
public Order GetOrder(int orderNumber)
{
...
}
public int CreateOrder(Order order)
{
...
}
}
Keyed服务注册:
//register the shelf implementation
builder.RegisterType<ShelfOrderManager>()
.Keyed(OrderType.Shelf)
.As<IOrderManager>();
//register the forecast implementation
builder.RegisterType<ForecastOrderManager>()
.Keyed(OrderType.Shelf)
.As<IOrderManager>();
注册工厂:
builder.RegisterType<IMS.POS.Services.Factory.ServiceFactory>()
.AsSelf()
.SingleInstance();
最终在控制器中使用它(或任何其他 class):
public class HomeController : BaseController
{
private readonly IContentManagerService _contentManagerService;
private readonly IViewModelService _viewModelService;
private readonly IApplicationSettingService _applicationSettingService;
private readonly IOrderManagerService _orderManagerService;
private readonly IServiceFactory _factory;
public HomeController(ServiceFactory.Factory factory,
IViewModelService viewModelService,
IContentManagerService contentManagerService,
IApplicationSettingService applicationSettingService)
{
//first assign the factory
//We keep the users Ordering Type in session - if the value is not set - default to Shelf ordering
_factory = factory(UIUserSession?.OrderingMode ?? OrderType.Shelf);
//now that I have a factory to get the implementation I need
_orderManagerService = _factory.Resolve<IOrderManagerService>();
//The rest of these are resolved by Autofac
_contentManagerService = contentManagerService;
_viewModelService = viewModelService;
_applicationSettingService = applicationSettingService;
}
}
我想对 Resolve 方法进行更多处理 - 但对于第一遍,这是有效的。有点工厂模式(在我们需要的地方),但仍然使用 Autofac 来完成大部分工作。