Unity 4(IOC)+ MVC 5无法在启动期间解析类型

时间:2017-03-07 22:16:55

标签: c# asp.net-mvc unity-container

我在Unity MVC 5中使用Unity 4(通过NuGet)。我已经修改了所有控制器和业务类,以通过其构造函数接收它们作为接口的依赖关系。

boostrapper类包含以下代码:

public class Bootstrapper
{
    public static IUnityContainer UnityContainer { get; set; }

    public static void Initialize()
    {
        InitializeUnity();
        RegisterTypes();
    }

    private static void InitializeUnity()
    {
        UnityContainer = UnityConfig.GetConfiguredContainer();
        DependencyResolver.SetResolver(new UnityDependencyResolver(UnityContainer));
    }

    private static void RegisterTypes()
    {
        new Core.Bootstrapper().RegisterTypes(UnityContainer);
    }
}

Core.Bootstrapper()。RegisterTypes的内容如下:

public IUnityContainer RegisterTypes(IUnityContainer container)
{
    // Register repositories
    container.RegisterType<IRepository, Repository>()
             .RegisterType<IBdeRepository, BdeRepository>()
             .RegisterType<IDocumentRepository, DocumentRepository>()
             .RegisterType<IHampRepository, HampRepository>()
             .RegisterType<IReportsRepository, ReportsRepository>()
             .RegisterType<ITransactionRepository, TransactionRepository>();

    // Register commands
    container.RegisterType<IBaseCommand, BaseCommand>()
             .RegisterType<IDailyLetterInfoUpdate, DailyLetterInfoUpdate>()
             .RegisterType<IDailyReconciliation, DailyReconciliation>()
             .RegisterType<InsertNewRows, InsertNewRows>()
             .RegisterType<IOrderDailyLM023, OrderDailyLM023>()
             .RegisterType<IOrderMonthlyLM012, OrderMonthlyLM012>()
             .RegisterType<IOrderMonthlyLM014, OrderMonthlyLM014>()
             .RegisterType<IPerformMonthlyRecastStatusUpdate, PerformMonthlyRecastStatusUpdate>()
             .RegisterType<IUpdateReissuedRecasts, UpdateReissuedRecasts>();

    // Register utility types
    container.RegisterType<ICsvExporter, CsvExporter>()
             .RegisterType<ILogger, Logger>();

    return container;
}

最后,UnityConfig.cs和UnityMvcActivator.cs保持不变,与NuGet包首次安装时的版本保持不变。

当我启动MVC应用程序时,我遇到以下错误消息:

  

依赖项的解析失败,输入=&#34; System.Web.Mvc.ITempDataProviderFactory&#34;,name =&#34;(none)&#34;。

     

在解析时发生异常。

     

异常是:InvalidOperationException - 当前类型System.Web.Mvc.ITempDataProviderFactory是一个接口,无法构造。你错过了类型映射吗?

它提到的课程似乎是MVC内部的东西,这让我相信我在配置Unity时错过了一些东西,或者我在这样做时遇到了什么。然而,对于我的生活,我无法弄清楚它是什么,尽管几乎可以找到关于StackOverflow上的错误消息的每个问题。 (到目前为止,所提出的解决方案都没有解决这个问题,但我很可能错过了一些东西。)

有人能看到我忽略的东西吗?我做错了什么?

更新

此处抛出异常:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapMvcAttributeRoutes(); // Exception thrown on this line.
}

2 个答案:

答案 0 :(得分:2)

这是一个略有不同的实现,包含MVC部分的特定排除,改编自UnityDependencyResolver.cs的代码

在启动时似乎是一个轻微的性能优势,但主要的是你不会引发10个例外。

/// <summary>
/// Resolves dependencies against the current DI container.
/// </summary>
public class UnityDependencyResolver : IDependencyResolver
{
    private readonly IUnityContainer container;
    // NB Not likely to be be big but will be frequent so use HashSet as O(1) lookup cost - see https://stackoverflow.com/questions/18651940/performance-benchmarking-of-contains-exists-and-any
    private readonly HashSet<Type> excludedTypes;

    /// <summary>
    /// Initializes a new instance of the <see cref="UnityDependencyResolver"/> class.
    /// </summary>
    /// <param name="container">The container.</param>
    public UnityDependencyResolver(IUnityContainer container)
    {
        this.container = container;
        excludedTypes = new HashSet<Type>();

        StandardExclusions();
    }

    /// <summary>
    /// Resolves an instance of the default requested type from the container.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the object to get from the container.</param>
    /// <returns>The requested object.</returns>
    public object GetService(Type serviceType)
    {
        if (typeof(IController).IsAssignableFrom(serviceType))
        {
            return container.Resolve(serviceType);
        }

        try
        {
            return excludedTypes.Contains(serviceType) ? null : container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    /// <summary>
    /// Resolves multiply registered services.
    /// </summary>
    /// <param name="serviceType">The type of the requested services.</param>
    /// <returns>The requested services.</returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return container.ResolveAll(serviceType);
    }

    public void Exclude<T>()
    {
        Exclude(typeof(T));
    }

    public void Exclude(Type type)
    {
        excludedTypes.Add(type);
    }

    public void Include<T>()
    {
        Include(typeof(T));
    }

    public void Include(Type type)
    {
        excludedTypes.Remove(type);
    }

    private void StandardExclusions()
    {
        Exclude<System.Web.Mvc.IControllerFactory>();
        Exclude<System.Web.Mvc.IControllerActivator>();
        Exclude<System.Web.Mvc.ITempDataProviderFactory>();
        Exclude<System.Web.Mvc.ITempDataProvider>();
        Exclude<System.Web.Mvc.Async.IAsyncActionInvokerFactory>();
        Exclude<System.Web.Mvc.IActionInvokerFactory>();
        Exclude<System.Web.Mvc.Async.IAsyncActionInvoker>();
        Exclude<System.Web.Mvc.IActionInvoker>();
        Exclude<System.Web.Mvc.IViewPageActivator>();
        Exclude<System.Web.Mvc.ModelMetadataProvider>();
    }
}

如果您覆盖了其中一个标准MVC接口,则可以使用

将其从排除列表中删除
var resolver = new UnityDependencyResolver(container);
resolver.Include<System.Web.Mvc.IActionInvoker>();

答案 1 :(得分:0)

我找到了我的特定场景的解决方案,这有点令人惊讶。

我必须滚动自己的DependencyResolver类并使用它来代替Unity提供的MVC解析器。我写的解析器如下:

using System;
using System.Collections.Generic;
using System.Web.Mvc;

using Microsoft.Practices.Unity;

/// <summary>
/// Resolves dependencies against the current DI container.
/// </summary>
public class UnityDependencyResolver : IDependencyResolver
{
    /// <summary>
    /// The container
    /// </summary>
    private readonly IUnityContainer container;

    /// <summary>
    /// Initializes a new instance of the <see cref="UnityDependencyResolver"/> class.
    /// </summary>
    /// <param name="container">The container.</param>
    public UnityDependencyResolver(IUnityContainer container) 
    {
        this.container = container;
    }

    /// <summary>
    /// Resolves an instance of the default requested type from the container.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the object to get from the container.</param>
    /// <returns>The requested object, if it can be resolved; otherwise, <c>null</c>.</returns>
    public object GetService(Type serviceType)
    {
        return this.container.IsRegistered(serviceType) 
            ? this.container.Resolve(serviceType) 
            : null;
    }

    /// <summary>
    /// Resolves multiply registered services.
    /// </summary>
    /// <param name="serviceType">The type of the requested services.</param>
    /// <returns>The requested services.</returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.container.ResolveAll(serviceType);
    }
}

最重要的部分是 GetService 的实现:如果它无法解析某个类型,它必须返回null,否则,MVC会逐渐消失并抛出异常消息,“XXX是一个界面,无法构建。你错过了绑定吗?”

这当然改变了Bootstrapper.cs中这行代码的行为:

DependencyResolver.SetResolver(
    new UnityDependencyResolver(UnityContainer));

它使用本地实现而不是默认的Unity实现。

这有点奇怪的是我正在绘制的示例应用程序在我的机器上本地工作,并且它没有使用自定义解析器。没有这个课,它工作正常。我不知道为什么会这样,这很令人不安(我更喜欢确定性的问题答案),但此时我很高兴我能回去工作。