如何使用Unity和DependencyResolver使用会话值

时间:2013-08-16 13:32:42

标签: asp.net-mvc architecture

我正在使用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正在尝试在会话构建之前解析控制器以及随后的服务。

我没有正确地考虑这个问题吗?每个步骤都感觉非常合理 - 在服务中使用会话凭证,使用控制器中的服务,使用解析器来帮助构建控制器 - 但是我一直在敲打墙头让它发挥作用。

3 个答案:

答案 0 :(得分:1)

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

取代:

container.RegisterType<IInventoryService, InventoryService>();

使用:

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

答案 1 :(得分:0)

使用Unity.Mvc4软件包似乎解决了这个问题,虽然我不清楚为什么。但是,我决定添加一个无参数的构造函数,并根据需要手动解析自己,而不是使用另一个软件包并隐藏我的问题:

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

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

答案 2 :(得分:-1)

下面是一个自定义IDependencyResolver,一旦我开始深入研究它是如何工作的,并且与IoC容器分辨率不同,这是非常直接的。您需要try / catches来捕获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>