首页 文章

与ASP.NET Core和MVC6(核心)的统一

提问于
浏览
16

Update 09.08.2018
Unity正在开发here但我没有时间来测试它如何与ASP.NET Core框架配合使用 .

Update 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 .

Intro
我正在使用ASP.NET和MVC构建Web应用程序 . 此应用程序依赖于某些服务(WCF服务,数据存储区服务等) . 现在为了保持良好和解耦,我想使用DI(Dependecy Injection)框架,特别是Unity .

Initial Research
我发现这个blog post但遗憾的是它不起作用 . 这个想法虽然很好 .
它基本上表示您不应将ServiceCollection中注册的所有服务注册到您自己的容器中,而应引用默认的ServiceProvider .
所以 . 如果需要解决某些问题,则调用默认的ServiceProvider,如果没有解析,则使用自定义UnityContainer解析类型 .

The Problems
MVC始终尝试使用默认的ServiceProvider解析Controller .
此外,我注意到即使控制器得到正确解析,我也永远不会"mix"依赖关系 . 现在,如果我想使用我的一个服务而且还要使用ASP的IOptions接口,则该类永远无法解析,因为这两个容器中没有一个具有两种类型的解析 .

What I need
所以回顾一下,我需要以下内容:

  • 我不需要将ASP.NET依赖项复制到UnityContainer中的设置

  • 可以解析我的MVC控制器的容器

  • 可以解析"mixed"依赖关系的容器

EDIT:
所以问题是我如何才能实现这些要点?

Environment
project.json:

enter image description here

2 回答

  • 7

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

    Use Unity with 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部分 .

    Resolving Controllers
    我找到了this blog post . 从中我了解到MVC使用IControllerActivator接口来处理Controller实例化 . 所以我写了我自己的,看起来像这样:

    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必须知道如何解析任何控制器!

    Next Problem: Use the default 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没有注册的东西,它只是询问默认的提供商 .
    我从几篇不同的文章中学到了所有这些

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

    Finally: Wire up
    现在在我的项目中,我使用不同的服务(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的知识,所以我希望我没有打破任何大的主要/模式,如果是这样,请告诉我!

  • 35

    对于ASP.Net Core 2.0,2.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应用程序中使用此解决方案并且运行良好 .

相关问题