我正在使用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正在尝试在会话构建之前解析控制器以及随后的服务。
我没有正确地考虑这个问题吗?每个步骤都感觉非常合理 - 在服务中使用会话凭证,使用控制器中的服务,使用解析器来帮助构建控制器 - 但是我一直在敲打墙头让它发挥作用。
答案 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>