基于运行时会话值从Autofac解析服务实现

时间:2016-06-16 18:11:00

标签: asp.net-mvc runtime autofac dependency-resolution

需要一些帮助来尝试解决在运行时根据参数解析服务实现的问题。换句话说,使用带DI的工厂模式。

我们已将Autofac连接到我们的MVC应用程序。我试图找出如何使用用户会话变量(调用它的订购类型)来用于依赖性解析器来解析服务的正确实现。

我们正在尝试做的一个例子。

该应用程序有两种“类型”的订购 - 真正的电子商务订购类型(添加到购物车的东西,结帐等)。

另一种称为预测排序。用户创建订单 - 但他们不会马上完成订单。他们通过审批程序然后履行。

底线是应用程序根据订单类型进行更改的数据模式和后端系统。

我想做的是:

  1. 我有IOrderManagerService

    public interface IOrderManagerService
    {
          Order GetOrder(int orderNumber);
          int CreateOrder(Order order);
    }
    
  2. 因为我们有两个排序“类型” - 我有两个IOrderManagerService的实现:

    public class ShelfOrderManager : IOrderManagerService
    {
        public Order GetOrder(int orderMumber)
        {
             ...code
        }
    
        public int CreateOrder(Order order)
        {
            ...code
        }
    }
    
  3.     public class ForecastOrderManager: IOrderManagerService
        {
            public Order GetOrder(int orderMumber)
            {
                 ...code
            }
    
            public int CreateOrder(Order order)
            {
                ...code
            }
        }
    
    1. 我的第一个问题是 - 在我的MVC应用程序中 - 我是否将这些实现注册为?

      builder.RegisterType<ShelfOrderManager>().As<IOrderManagerService>();
      builder.RegisterType<ForecastOrderManager>().As<IOrderManagerService>();
      
    2. 我们计划做的是在用户会话中坚持用户选择的订购类型。当用户想要查看订单状态时 - 根据他们选择的订单“类型” - 我需要解析器为控制器提供正确的实现。

      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);
            }
      }
      
    3. 我已经准备好the delegate factorythis answer很好地解释了这个概念。

      问题是运行时参数用于构造服务并在运行时解析。即

           var service = resolvedServiceClass.Factory("runtime parameter")
      

      所有这一切都是给我“服务”在构造函数中使用“运行时参数”。

      我也看过Keyed or Named分辨率。

      起初我以为我可以将这两种技术结合起来 - 但是控制器依赖于接口 - 而不是具体的实现。 (应该如此)

      如何解决这个问题的任何想法都会被很多赞赏。

2 个答案:

答案 0 :(得分:1)

事实证明我们很接近。 @Andrei与我们的目标一致。我将为下一个遇到此问题的人解释下面的答案。

回顾一下问题 - 我需要在运行时使用Autofac解析接口的具体具体实现。这通常由工厂模式解决 - 但我们已经实施了DI。

解决方案是使用两者。使用delegate factory Autofac支持,我创建了一个简单的工厂类。

我选择私下解决组件上下文

   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)
    {
       ...
    }
}

密钥服务的注册:

        //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();

最后在控制器(或任何其他类)中使用它:

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来完成大部分工作。

答案 1 :(得分:0)

我不会依赖Autofac。 IOC用于解析依赖关系并为其提供实现,您需要根据决策标志调用同一接口的不同实现。

我基本上会使用一个简单的工厂,比如一个带有2个静态方法的类,当你知道决定是什么时,可以调用你需要的任何实现。这为您提供了所需的运行时解析器。保持简单我会说。

据说这似乎还有另一种选择。看看“按上下文选择”选项,也许你可以重新设计你的课程来利用这个:http://docs.autofac.org/en/latest/faq/select-by-context.html