【问题标题】:Unity with ASP.NET Core and MVC6 (Core)Unity 与 ASP.NET Core 和 MVC6 (Core)
【发布时间】:2017-01-03 13:01:07
【问题描述】:

更新 09.08.2018
Unity 正在开发中here,但我还没有时间测试它如何与 ASP.NET Core 框架配合使用。

15.03.2018 更新
此解决方案针对在使用 .NET Framework 4.5.2 NOT .NET Core Framework 时将 ASP.NET Core v1 与 Unity 结合使用的特定问题。我不得不使用这种设置,因为我需要一些 .Net 4.5.2 DLL,但对于任何重新开始的人,我不推荐这种方法。 Unity 也没有进一步开发(据我所知),所以我建议将 Autofac 框架用于新项目。请参阅此Post,了解有关如何执行此操作的更多信息。

简介
我正在使用带有 MVC 的 ASP.NET 构建一个 Web 应用程序。此应用程序依赖于某些服务(WCF 服务、数据存储服务等)。现在,为了保持良好和解耦,我想使用 DI(依赖注入)框架,特别是 Unity。

初步研究
我找到了这个blog post,但遗憾的是它不起作用。这个想法虽然很好。
它基本上说您不应该将在 ServiceCollection 中注册的所有服务都注册到您自己的容器中,而是引用默认的 ServiceProvider。
所以。如果需要解决某些问题,则会调用默认的 ServiceProvider,如果它没有解决方案,则将使用您的自定义 UnityContainer 解决该类型。

问题
MVC 总是尝试使用默认的 ServiceProvider 来解析 Controller。
另外,我注意到即使控制器能够正确解析,我也永远无法“混合”依赖项。现在,如果我想使用我的服务之一以及来自 ASP 的 IOptions 接口,则永远无法解析该类,因为这两个容器中没有一个具有两种类型的解析。

我需要什么
所以回顾一下,我需要以下几点:

  • 不需要将 ASP.NET 依赖项复制到 UnityContainer 中的设置
  • 一个可以解析我的 MVC 控制器的容器
  • 一个可以解析“混合”依赖的容器

编辑:
所以问题是我怎样才能达到这些点?

环境
project.json:

【问题讨论】:

  • 不是一个问题
  • 现在好点了吗?如果不是,我应该如何改写它?
  • Unity 正在开发中,你的错误。 github.com/unitycontainer/container
  • hm 好像我错过了。无论如何,帖子都会更新
  • 请勿发布代码、数据、错误消息等的图片 - 将文本复制或输入到问题中。 How to Ask

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


【解决方案1】:

所以经过一些研究,我想出了以下解决问题的方法:

将 Unity 与 ASP 结合使用
为了能够在 ASP 中使用 Unity,我需要一个自定义 IServiceProvider (ASP Documentation),所以我为 IUnityContainer 编写了一个包装器,如下所示

public class UnityServiceProvider : IServiceProvider
{
    private IUnityContainer _container;

    public IUnityContainer UnityContainer => _container;

    public UnityServiceProvider()
    {
        _container = new UnityContainer();
    }

    #region Implementation of IServiceProvider

    /// <summary>Gets the service object of the specified type.</summary>
    /// <returns>A service object of type <paramref name="serviceType" />.-or- null if there is no service object of type <paramref name="serviceType" />.</returns>
    /// <param name="serviceType">An object that specifies the type of service object to get. </param>
    public object GetService(Type serviceType)
    {
        //Delegates the GetService to the Containers Resolve method
        return _container.Resolve(serviceType);
    }

    #endregion
}

我还必须在我的 Startup 类中更改 ConfigureServices 方法的签名:

public void ConfigureServices(IServiceCollection services)

到这里:

public IServiceProvider ConfigureServices(IServiceCollection services)

现在我可以返回我的自定义 IServiceProvider,它将被使用而不是默认的。
完整的 ConfigureServices 方法显示在底部的 Wire up 部分中。

解析控制器
我找到了this blog post。从中我了解到 MVC 使用 IControllerActivator 接口来处理控制器实例化。所以我写了自己的,看起来像这样:

public class UnityControllerActivator : IControllerActivator
{
    private IUnityContainer _unityContainer;

    public UnityControllerActivator(IUnityContainer container)
    {
        _unityContainer = container;
    }

    #region Implementation of IControllerActivator

    public object Create(ControllerContext context)
    {
        return _unityContainer.Resolve(context.ActionDescriptor.ControllerTypeInfo.AsType());
    }


    public void Release(ControllerContext context, object controller)
    {
        //ignored
    }

    #endregion
}

现在,如果一个 Controller 类被激活,它将被我的 UnityContainer 实例化。因此我的 UnityContainer 必须知道如何解析任何控制器!

下一个问题:使用默认的 IServiceProvider
现在,如果我在 ASP.NET 中注册 Mvc 等服务,我通常会这样做:

services.AddMvc();

现在,如果我使用 UnityContainer,所有 MVC 依赖项都无法解析,因为它们没有注册。所以我可以注册它们(比如 AutoFac)或者我可以创建一个 UnityContainerExtension。我选择了扩展并提出了以下两个类别:
UnityFallbackProviderExtension

public class UnityFallbackProviderExtension : UnityContainerExtension
{
    #region Const

    ///Used for Resolving the Default Container inside the UnityFallbackProviderStrategy class
    public const string FALLBACK_PROVIDER_NAME = "UnityFallbackProvider";

    #endregion

    #region Vars

    // The default Service Provider so I can Register it to the IUnityContainer
    private IServiceProvider _defaultServiceProvider;

    #endregion

    #region Constructors

    /// <summary>
    /// Creates a new instance of the UnityFallbackProviderExtension class
    /// </summary>
    /// <param name="defaultServiceProvider">The default Provider used to fall back to</param>
    public UnityFallbackProviderExtension(IServiceProvider defaultServiceProvider)
    {
        _defaultServiceProvider = defaultServiceProvider;
    }

    #endregion

    #region Overrides of UnityContainerExtension

    /// <summary>
    /// Initializes the container with this extension's functionality.
    /// </summary>
    /// <remarks>
    /// When overridden in a derived class, this method will modify the given
    /// <see cref="T:Microsoft.Practices.Unity.ExtensionContext" /> by adding strategies, policies, etc. to
    /// install it's functions into the container.</remarks>
    protected override void Initialize()
    {
        // Register the default IServiceProvider with a name.
        // Now the UnityFallbackProviderStrategy can Resolve the default Provider if needed
        Context.Container.RegisterInstance(FALLBACK_PROVIDER_NAME, _defaultServiceProvider);

        // Create the UnityFallbackProviderStrategy with our UnityContainer
        var strategy = new UnityFallbackProviderStrategy(Context.Container);

        // Adding the UnityFallbackProviderStrategy to be executed with the PreCreation LifeCycleHook
        // PreCreation because if it isnt registerd with the IUnityContainer there will be an Exception
        // Now if the IUnityContainer "magically" gets a Instance of a Type it will accept it and move on
        Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
    }

    #endregion
}


UnityFallbackProviderStrategy

public class UnityFallbackProviderStrategy : BuilderStrategy
{
    private IUnityContainer _container;

    public UnityFallbackProviderStrategy(IUnityContainer container)
    {
        _container = container;
    }

    #region Overrides of BuilderStrategy

    /// <summary>
    /// Called during the chain of responsibility for a build operation. The
    /// PreBuildUp method is called when the chain is being executed in the
    /// forward direction.
    /// </summary>
    /// <param name="context">Context of the build operation.</param>
    public override void PreBuildUp(IBuilderContext context)
    {
        NamedTypeBuildKey key = context.OriginalBuildKey;

        // Checking if the Type we are resolving is registered with the Container
        if (!_container.IsRegistered(key.Type))
        {
            // If not we first get our default IServiceProvider and then try to resolve the type with it
            // Then we save the Type in the Existing Property of IBuilderContext to tell Unity
            // that it doesnt need to resolve the Type
            context.Existing = _container.Resolve<IServiceProvider>(UnityFallbackProviderExtension.FALLBACK_PROVIDER_NAME).GetService(key.Type);
        }

        // Otherwise we do the default stuff
        base.PreBuildUp(context);
    }

    #endregion
}

现在,如果我的 UnityContainer 没有注册某些东西,它只需向默认提供者询问它。
我从几篇不同的文章中学到了所有这些

这种方法的好处是我现在还可以“混合”依赖项。如果我需要我的任何服务和来自 ASP 的 IOptions 接口,我的 UnityContainer 将解析所有这些依赖项并将它们注入我的控制器!
唯一要记住的是,如果我使用我自己的任何依赖项,我必须向 Unity 注册我的 Controller 类,因为默认的 IServiceProvider 不能再解析我的控制器依赖项。

最后:连线
现在在我的项目中,我使用不同的服务(ASP 选项、带有选项的 MVC)。为了让这一切正常工作,我的 ConfigureServices 方法现在看起来像这样:

public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        // Add all the ASP services here
        // #region ASP
        services.AddOptions();
        services.Configure<WcfOptions>(Configuration.GetSection("wcfOptions"));

        var globalAuthFilter = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();

        services.AddMvc(options => { options.Filters.Add(new AuthorizeFilter(globalAuthFilter)); })
                .AddJsonOptions
            (
                options => options.SerializerSettings.ContractResolver = new DefaultContractResolver()
            );
        // #endregion ASP

        // Creating the UnityServiceProvider
        var unityServiceProvider = new UnityServiceProvider();

        IUnityContainer container = unityServiceProvider.UnityContainer;

        // Adding the Controller Activator
        // Caution!!! Do this before you Build the ServiceProvider !!!
        services.AddSingleton<IControllerActivator>(new UnityControllerActivator(container));

        //Now build the Service Provider
        var defaultProvider = services.BuildServiceProvider();

        // Configure UnityContainer
        // #region Unity

        //Add the Fallback extension with the default provider
        container.AddExtension(new UnityFallbackProviderExtension(defaultProvider));

        // Register custom Types here

        container.RegisterType<ITest, Test>();

        container.RegisterType<HomeController>();
        container.RegisterType<AuthController>();

        // #endregion Unity

        return unityServiceProvider;
    }

由于我在过去一周了解了大部分关于 DI 的知识,所以我希望我没有破坏任何大的 Pricipal/Pattern 如果是这样,请告诉我!

【讨论】:

  • 非常感谢这个解决方案。为我节省了几个小时!
  • 您的解决方案让我在 Internet 上进行搜索,这些链接可以为您提供备用机制的替代方案:fueltravel.com/blog/…dzimchuk.net/bring-your-own-di-container-to-aspnet-5-unity 感谢您和这些文章的作者,谢谢
  • @Leonardo 不确定 IServer 接口来自哪里,但它不是来自这部分代码。在 FallbackProvider 上,您的意思是,如果该类型已在我们的容器中注册,您将去请求另一个容器提供它。真的没有道理,但也许它的 bc 你设置了不同的东西?
  • @Leonardo 好吧,它是为 .Net Core 1.0 编写的,所以是的,它可能不适用于 2.0。问题是我不再真正使用 Unity(它没有进一步开发)并且认为花时间在它上面是没有意义的。如果您没有真正设置 Unity,请尝试 Autofac。由于 ASP.NET Core 在后台使用它,因此存在复制所有注册的扩展,因此您不必使用像这样的解决方法。 (到目前为止,任何它都像魅力一样)
  • @Leonardo 这也是一个有点古怪的设置,因为实际的框架是 .Net 4.5.2(project.json 第 30 行),然后引入了核心包。在我的场景中这是需要的,因为我想使用一些我为 .NET 4.5.2 编写的 DLL,但是如果你开始一个新项目,我不会再推荐这种方法了
【解决方案2】:

对于 ASP.Net Core 2.0、2.1、2.2、3.1 和 Unity,Unity 作者提供了官方解决方案作为 NuGet 包:NuGetPackage

这里是带有示例的 Git 存储库:Git repo

使用很简单(来自Git repo主页):

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
       .UseUnityServiceProvider()   <---- Add this line
       .UseStartup<Startup>()
       .Build();

here 是用于 ASP.Net Core 的 Unity DI 示例。

我在我的 ASP.Net Core 应用程序中使用此解决方案并且效果很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-17
    • 1970-01-01
    • 2019-11-02
    • 1970-01-01
    • 2016-10-07
    • 2018-11-30
    • 1970-01-01
    • 2016-05-14
    相关资源
    最近更新 更多