2

我正在使用 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 正在尝试解析控制器,然后是服务,甚至在会话构建之前。

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

4

3 回答 3

1

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

代替:

container.RegisterType<IInventoryService, InventoryService>();

和:

container.RegisterType<IInventoryService>(new InjectionFactory(c =>
    new InventoryService(SessionManager.ServiceKey)));
于 2013-08-16T14:48:28.810 回答
0

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

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

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

于 2013-08-16T19:52:54.767 回答
-1

下面是一个自定义的 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>
于 2013-08-17T00:30:49.437 回答