【问题标题】:Resolve Service Implementation from Autofac based on Runtime Session Value根据运行时会话值从 Autofac 解析服务实现
【发布时间】:2016-06-16 18:11:00
【问题描述】:

在尝试解决基于参数在运行时解决服务实现的问题时需要一些帮助。换句话说,使用带有 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
        }
    }
    

    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);
          }
    }
    

我已经准备好了the delegate factorythis answer 很好地解释了这个概念。

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

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

所有这一切都会为我提供在构造函数中使用“运行时参数”的“服务”。

我也看过Keyed or Named 分辨率。

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

任何有关如何解决此问题的想法将不胜感激非常

【问题讨论】:

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


    【解决方案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)
        {
           ...
        }
    }
    

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

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

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

    【讨论】:

    • 有趣的是——我的一位开发人员在我们完成这个概念后发现了this。不完全相同 - 但非常接近。我们的控制器构造函数可能会忙于处理许多依赖服务 - 我想我会坚持使用 ServiceFactory.Factory 而不是 func 工厂来处理每个需要一次解析的服务。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-22
      • 2018-08-31
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      • 1970-01-01
      • 2013-12-15
      相关资源
      最近更新 更多