与ASP.NET Core和MVC6(核心)实现统一 [英] Unity with ASP.NET Core and MVC6 (Core)

查看:157
本文介绍了与ASP.NET Core和MVC6(核心)实现统一的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新09.08.2018
此处正在开发Unity,但我还没有时间测试它如何在ASP中发挥作用. NET Core框架.

Update 09.08.2018
Unity is being developed here but I haven't had the time to test how it plays with the ASP.NET Core framework.

更新15.03.2018
该解决方案是针对将ASP.NET Core v1与Unity一起使用,同时使用.NET Framework 4.5.2 NOT .NET Core框架的特定问题.我必须使用此设置,因为我需要一些.Net 4.5.2 DLL,但是对于任何重新开始的人,我都不推荐这种方法.同样,据我所知,Unity还没有进一步开发,因此我建议对新项目使用Autofac Framework.请参阅此发布有关如何执行此操作的更多信息.

Update 15.03.2018
This solution is for the specific problem of using ASP.NET Core v1 with Unity while using the .NET Framework 4.5.2 NOT the .NET Core Framework. I had to use this setup since I needed some .Net 4.5.2 DLLs but for anyone starting afresh I would not recommend this approach. Also Unity is not being developed any further (to my knowlage) so I would recommend using the Autofac Framework for new projects. See this Post for more info on how to do that.

简介
我正在使用带有MVC的ASP.NET构建Web应用程序.此应用程序取决于某些服务(WCF服务,数据存储服务等).现在,为了使事情保持良好状态和分离,我想使用DI(依赖注入)框架,特别是Unity.

初步研究
我发现了这个博客帖子,但遗憾的是它无法正常工作.但是,这个主意很好.
基本上,您不应将在ServiceCollection中注册的所有服务注册到自己的容器中,而应引用默认的ServiceProvider.
如果需要解决某些问题,则将调用默认的ServiceProvider,如果它没有解决方案,则将使用您的自定义UnityContainer来解析类型.

问题
MVC始终尝试使用默认的ServiceProvider解析控制器.
我还注意到,即使控制器会得到正确解决,我也永远无法混合"到控制面板上.依赖关系.现在,如果我要使用我的服务之一,也要使用ASP的IOptions接口,则该类将永远无法解析,因为这两个容器中的任何一个都不具有针对两种类型的解析度.

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

Intro
I am building a Web Application using ASP.NET with MVC. This Application depends on certain services (a WCF Service a Datastore service etc). Now to keep things nice and decoupled I want to use a DI (Dependecy Injection) Framework, specifically Unity.

Initial Research
I found this blog post but sadly its not working. The idea though is nice.
It basically says that you should not register all the services registered in the ServiceCollection into your own container, but rather reference the default ServiceProvider.
So. if something needs to be resolved the default ServiceProvider is called and in case it has no resolution the type will be resolved using your custom UnityContainer.

The Problems
MVC always tries to resolve the Controller with the default ServiceProvider.
Also, I noticed that even if the Controller would get resolved correctly, I can never "mix" Dependencies. Now, if I want to use one of my Services but also an IOptions interface from ASP the class can never be resolved because not one of those two containers has resolutions for both types.

What I need
So to recap I need the following things:

  • 不需要将ASP.NET依赖项复制到UnityContainer中的设置
  • 可以解析我的MVC控制器的容器
  • 可以解决混合"依赖关系的容器


那么问题是如何实现这些要点?


So the question is how can I achieve these points ?

环境
project.json:

Environment
project.json:

推荐答案

因此,在进行了一些研究之后,我针对自己的问题提出了以下解决方案:

在ASP中使用Unity
为了能够在ASP中使用Unity,我需要一个自定义IServiceProvider(

So after some research I came up with the following solutions to my problems:

Use Unity with ASP
To be able to use Unity with ASP I needed a custom IServiceProvider (ASP Documentation) so I wrote a wrapper for the IUnityContainer which looks like this

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方法的签名:

Also I had to change the Signature of the ConfigureServices method in my Startup class from this:

public void ConfigureServices(IServiceCollection services)

对此:

public IServiceProvider ConfigureServices(IServiceCollection services)

现在,我可以返回自定义的IServiceProvider,它将代替默认的IServiceProvider.
完整的ConfigureServices方法显示在底部的连接"部分.

解析控制器
我发现了
此博客文章.从中我了解到MVC使用IControllerActivator接口来处理Controller实例化.所以我写了我自己的,看起来像这样:

Now I can return my custom IServiceProvider and it will be used instead of the default one.
The full ConfigureServices Method is shown in the Wire up section at the bottom.

Resolving Controllers
I found this blog post. From it I learned that MVC uses an IControllerActivator interface to handle Controller instantiation. So I wrote my own which looks like this:

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之类的服务,通常我会这样做:

Now if a Controller class is activated it will be instatiated with my UnityContainer. Therefore my UnityContainer must know how to Resolve any Controller!

Next Problem: Use the default IServiceProvider
Now if I register services such as Mvc in ASP.NET I normally would do it like this:

services.AddMvc();

现在,如果我使用UnityContainer,则所有MVC依赖关系都无法解析,因为它们尚未注册.因此,我可以注册它们(例如AutoFac),也可以创建UnityContainerExtension.我选择了扩展程序,并提出了以下两个方面:
UnityFallbackProviderExtension

Now if I use a UnityContainer all the MVC Dependencies could not be Resolved because they aren't Registered. So I can either Register them (like AutoFac) or I can create a UnityContainerExtension. I opted for the Extension and came up with following two clases :
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没有针对某项的注册,则只需向默认提供者询问即可.
我从几篇不同的文章中学到了所有

Now if my UnityContainer has no Registration for something it just ask the default Provider for it.
I learned all of this from several different articles

  • MSDN Unity article
  • Auto-Mocking Unity Container Extension
  • Custom Object Factory Unity Extension

这种方法的好处是我现在也可以混合"依赖项.如果我需要任何服务以及来自ASP的IOptions接口,我的UnityContainer都将解决所有这些依赖关系并将其注入到我的控制器中!!!
唯一要记住的是,如果我使用自己的任何依赖关系,都必须向Unity注册我的Controller类,因为默认的IServiceProvider无法再解析我的Controllers依赖关系.

最后:连接
现在在我的项目中,我使用不同的服务(ASP选项,带有选项的MVC).要使其全部正常工作,我的ConfigureServices方法现在看起来像这样:

The nice thing about this approach is that I can also "mix" Dependencies now. If I need any of my Services AND an IOptions Interface from ASP my UnityContainer will resolve all of these Dependencies and Inject them into my Controller !!!
The only thing to remember is that if I use any of my own Dependencies I have to register my Controller class with Unity because the default IServiceProvider can no longer Resolve my Controllers Dependencies.

Finally: Wire up
Now in my project I use different services (ASP Options, MVC with options). To make it all work my ConfigureServices Method looks like this now:

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,如果有的话,请告诉我!

Since I learned most of what I know about DI in the past week I hope I didnt break any big Pricipal/Pattern if so please tell me!

这篇关于与ASP.NET Core和MVC6(核心)实现统一的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆