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

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

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

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

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

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

我需要的
所以回顾一下,我需要以下内容:

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

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

环境
project.json:
在此处输入图像描述

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

使用Unity与ASP
为了能够与ASP一起使用Unity,我需要一个自定义的IServiceProvider( ASP文档 ),所以我为IUnityContainer编写了一个包装器,看起来像这样

public class UnityServiceProvider : IServiceProvider { private IUnityContainer _container; public IUnityContainer UnityContainer => _container; public UnityServiceProvider() { _container = new UnityContainer(); } #region Implementation of IServiceProvider /// Gets the service object of the specified type. /// A service object of type .-or- null if there is no service object of type . /// An object that specifies the type of service object to get.  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部分。

解析控制器
我找到了这篇博文 。 从中我了解到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必须知道如何解析任何控制器!

下一个问题:使用默认的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 ///  /// Creates a new instance of the UnityFallbackProviderExtension class ///  /// The default Provider used to fall back to public UnityFallbackProviderExtension(IServiceProvider defaultServiceProvider) { _defaultServiceProvider = defaultServiceProvider; } #endregion #region Overrides of UnityContainerExtension ///  /// Initializes the container with this extension's functionality. ///  ///  /// When overridden in a derived class, this method will modify the given ///  by adding strategies, policies, etc. to /// install it's functions into the container. 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 ///  /// 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. ///  /// Context of the build operation. 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(UnityFallbackProviderExtension.FALLBACK_PROVIDER_NAME).GetService(key.Type); } // Otherwise we do the default stuff base.PreBuildUp(context); } #endregion } 

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

  • MSDN Unity文章
  • 自动模拟Unity容器扩展
  • 自定义对象工厂Unity扩展

这种方法的好处是我现在也可以“混合”依赖关系。 如果我需要来自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(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(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(); container.RegisterType(); container.RegisterType(); // #endregion Unity return unityServiceProvider; } 

因为我在过去一周里学到了大部分关于DI的知识,所以我希望我没有打破任何大的主要/模式,如果是这样,请告诉我!

对于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() .Build(); 

以下是使用Unity DI for ASP.Net Core的示例。

我在我的ASP.Net Core应用程序中使用此解决方案并且运行良好。