Asp.net mvc 如何将会话值与Unity和DependencyResolver一起使用

Asp.net mvc 如何将会话值与Unity和DependencyResolver一起使用,asp.net-mvc,architecture,Asp.net Mvc,Architecture,我正在使用MVC4和Unity 2.1。我的服务需要基于从会话状态检索到的凭据的服务密钥 我注册我的服务如下: 在我的网站中,当我需要一项服务时,我会使用一个服务定位器,该定位器使用会话中的凭据自动组合服务密钥 public static T Resolve<T>(ServiceKey serviceKey = null) { if (serviceKey == null) { serviceKey = SessionM

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

我注册我的服务如下:

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

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仍然抱怨在尝试实例化库存控制器时没有找到无参数构造函数。我想这是因为我还没有在Unity中注册服务密钥。但如果我尝试这样做,我发现MVC正在尝试在会话构建之前解析控制器,然后解析服务


我想得不对吗?每一步感觉都很合理——在服务中使用会话凭据,在控制器中使用服务,使用解析器帮助构建控制器——但我一直在竭尽全力让它发挥作用。

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

替换:

container.RegisterType<IInventoryService, InventoryService>();
container.RegisterType();
与:

container.RegisterType(新注入工厂(c=>
新的InventoryService(SessionManager.ServiceKey));

使用Unity.Mvc4包似乎解决了这个问题,但我不清楚原因。但我决定添加一个无参数构造函数,根据需要手动解析自身,而不是使用另一个包来隐藏我的问题:

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

它仍然允许控制器的单元测试(通过注入)当调用无参数构造函数时,解析发生在何处是透明的。

下面是一个自定义的IDependencyResolver,在我开始深入了解它的工作原理时,它相当直截了当,不同于IoC容器解析。您需要try/catch来捕获MVC尝试的IControl解析lerActivator(来源:)。如果无法解析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>();
            }
        }
    }
}
以下是我的宏伟蓝图:

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
<p>@ViewBag.SomeData</p>
@{
ViewBag.Title=“Index”;
}
指数
@ViewBag.SomeData


很有趣。这有助于澄清问题,尽管我仍然会遇到一个错误,即即使在替换了那行代码之后也找不到无参数构造函数。这可能超出了这个问题的范围,因为会话数据现在似乎已经得到了解释。也许是我的依赖关系图中的其他内容。你不应该这样做使用无参数构造函数。您应该通过IoC容器解析对象,并让它注入依赖项(以及依赖项的依赖项等)。“container.resolve();”将返回带有服务密钥的InventoryService。如果您想要无参数构造函数,还可以注入属性值。我个人更喜欢构造函数注入,但以下链接应该有助于选择:对,这就是令人费解的地方。我的InventoryController上没有无参数构造函数——我注入服务,这反过来又需要一个服务密钥。您关于注入工厂的观点似乎解决了服务的会话问题,但MVC仍在抱怨InventoryController上没有无参数构造函数(这是真的……但考虑到Unity的使用,这不应该是个问题).无论如何,我需要为一个即将到来的项目做这件事,所以今晚我将进一步深入研究这件事。同时,看看这个实验室是否有帮助:…别忘了从Global.asax.cs=)中调用UnityConfig)
container.RegisterType<IInventoryService, InventoryService>();
container.RegisterType<IInventoryService>(new InjectionFactory(c =>
    new InventoryService(SessionManager.ServiceKey)));
public InventoryController() : this (MvcDependencyFactory.Resolve<IInventoryService>(SessionManger.ServiceKey) { }
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>