【问题标题】:How to use session values with Unity and DependencyResolver如何在 Unity 和 DependencyResolver 中使用会话值
【发布时间】:2013-08-18 22:25:45
【问题描述】:

我正在使用 MVC4 和 Unity 2.1。我的服务需要基于从会话状态检索到的凭据的服务密钥。

我这样注册我的服务:

container.RegisterType<IInventoryService, InventoryService>();

InventoryService 的构造函数同样简单:

public InventoryService(ServiceKey serviceKey) {  ...  }

在我的网站中,当我需要一项服务时,我会使用服务定位器,它会使用会话中的凭据自动组合服务密钥。

public static T Resolve<T>(ServiceKey serviceKey = null)
    {
        if (serviceKey == null)
        {
            serviceKey = SessionManager.ServiceKey;
        }

        var parameterOverride = new ParameterOverride(SERVICEKEY_PARAMETERNAME, serviceKey);

        return Resolve<T>(null, parameterOverride);
    }

这运作良好。问题是我现在正在将我的站点转换为 MVC,并尝试使用一个简单的依赖解析器将服务注入控制器,该依赖解析器使用我现有的服务定位器(依赖工厂):

public class CustomDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        return MvcDependencyFactory.Resolve(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return MvcDependencyFactory.ResolveAll(serviceType);
    }
}

我的控制器看起来像:

public InventoryController(IInventoryService inventoryService) { ... }

问题是 MVC 在尝试实例化库存控制器时仍然抱怨找不到无参数构造函数。我认为这是因为我没有在 Unity 中注册服务密钥。但是如果我尝试这样做,我发现 MVC 正在尝试解析控制器,然后是服务,甚至在会话构建之前。

我没有正确地考虑这个吗?每一步都感觉很合理——在服务中使用会话凭据,在控制器中使用服务,使用解析器来帮助构建控制器——但我一直在努力让这个工作正常进行。

【问题讨论】:

    标签: asp.net-mvc architecture


    【解决方案1】:

    您可以使用 Unity 中的 InjectionFactory (Microsoft.Practices.Unity.InjectionFactory) 来指定一个函数来处理您的依赖关系的解析。只有在解决依赖关系时才会执行此函数。在下面的示例中,“c”是作为参数传递的 Unity 容器,以便您可以在函数中执行其他解析。

    替换:

    container.RegisterType<IInventoryService, InventoryService>();
    

    与:

    container.RegisterType<IInventoryService>(new InjectionFactory(c =>
        new InventoryService(SessionManager.ServiceKey)));
    

    【讨论】:

    • 有趣。这有助于澄清,尽管即使在替换了那行代码之后,我仍然收到关于无法找到无参数构造函数的错误。也许这超出了这个问题的范围,因为现在似乎已经考虑了会话数据。也许它是我的依赖图中的其他东西。
    • 你不应该有一个无参数的构造函数。您应该通过您的 IoC 容器解析您的对象并让它注入依赖项(以及您的依赖项的依赖项等等)。 “container.Resolve();”将使用您的服务密钥返回 InventoryService。
    • 如果你想要一个无参数的构造函数,你也可以注入属性值。我个人更喜欢构造函数注入,但以下链接应该有助于替代方案:stackoverflow.com/questions/515028/…
    • 对,这就是令人费解的地方。我的 InventoryController 上没有无参数构造函数——我注入了服务,而服务又需要一个服务密钥。您关于注入工厂的观点似乎解决了服务的会话问题,但 MVC 仍然抱怨在 InventoryController 上没有无参数构造函数(这是真的……但考虑到使用 Unity,这不应该是问题)。
    • 无论如何我都需要为即将到来的项目做这件事,所以今晚我会再深入研究一下。同时,看看这个实验室是否有帮助:asp.net/mvc/tutorials/hands-on-labs/…
    【解决方案2】:

    使用 Unity.Mvc4 包似乎可以解决问题,但我不清楚为什么。但与其使用另一个包并隐藏我的问题,我决定添加一个无参数构造函数,根据需要手动解析:

    public InventoryController() : this (MvcDependencyFactory.Resolve<IInventoryService>(SessionManger.ServiceKey) { }
    

    它仍然允许对控制器进行单元测试(通过注入),同时在调用无参数构造函数时对发生解析的位置透明。

    【讨论】:

      【解决方案3】:

      下面是一个自定义的 IDependencyResolver,一旦我开始深入研究它的工作原理以及与 IoC 容器解析的不同之处,它就相当简单了。您需要 try/catch 来捕获 MVC 对 IControllerActivator 的尝试解析(来源:http://www.devtrends.co.uk/blog/do-not-implement-icontrolleractivator-in-asp.net-mvc-3)。如果 IControllerActivator 无法解析,则将为您的控制器查询您的自定义 IDependencyResolver(它将使用您选择的 IoC 容器)。

      我将以下类添加到我的基本 MVC4 的 App_Start 文件夹中:

      using System;
      using System.Collections.Generic;
      using System.Web.Mvc;
      using Microsoft.Practices.Unity;
      using Sample.Web.Controllers;
      
      namespace Sample.Web.App_Start
      {
          public static class UnityConfig
          {
              public static void ConfigureContainer()
              {
                  IUnityContainer container = BuildUnityContainer();
                  DependencyResolver.SetResolver(new UnityDependencyResolver(container));
              }
      
              private static IUnityContainer BuildUnityContainer()
              {
                  var container = new UnityContainer();
      
                  container.RegisterType<IHomeService>(new InjectionFactory( c =>
                      new HomeService("this string is a dependency.")));
                  container.RegisterType<IController, HomeController>("Home");
      
                  return container;
              }
          }
      
          public class UnityDependencyResolver : IDependencyResolver
          {
              private readonly IUnityContainer _container;
      
              public UnityDependencyResolver(IUnityContainer container)
              {
                  _container = container;
              }
      
              public object GetService(Type serviceType)
              {
                  try
                  {
                      return _container.Resolve(serviceType);
                  }
                  catch (ResolutionFailedException)
                  {
                      return null;
                  }
              }
      
              public IEnumerable<object> GetServices(Type serviceType)
              {
                  try
                  {
                      return _container.ResolveAll(serviceType);
                  }
                  catch (ResolutionFailedException)
                  {
                      return new List<object>();
                  }
              }
          }
      }
      

      这是我的简单控制器:

      using System.Web.Mvc;
      
      namespace Sample.Web.Controllers
      {
          public class HomeController : Controller
          {
              private readonly IHomeService _service;
      
              public HomeController(IHomeService service)
              {
                  _service = service;
              }
      
              public ActionResult Index()
              {
                  ViewBag.SomeData = _service.GetSomeData();
                  return View();
              }
          }
      
          public interface IHomeService
          {
              string GetSomeData();
          }
      
          public class HomeService : IHomeService
          {
              private readonly string _data;
      
              public HomeService(string data)
              {
                  _data = data;
              }
      
              public string GetSomeData()
              {
                  return _data;
              }
          }
      }
      

      这是我的超大视野:

      @{
          ViewBag.Title = "Index";
      }
      
      <h2>Index</h2>
      <p>@ViewBag.SomeData</p>
      

      【讨论】:

      • ...别忘了从你的 Global.asax.cs 调用 UnityConfig =)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-29
      相关资源
      最近更新 更多