簡體   English   中英

與 ASP.NET Core 和 MVC6(核心)的統一

[英]Unity with ASP.NET Core and MVC6 (Core)

2018 年 8 月 9 日更新
Unity 正在這里開發但我沒有時間測試它如何與 ASP.NET Core 框架一起使用。

2018 年 3 月 15 日更新
此解決方案適用於在使用 .NET Framework 4.5.2 而不是.NET Core Framework 時將 ASP.NET Core v1 與 Unity 一起使用的特定問題。 我不得不使用這個設置,因為我需要一些 .Net 4.5.2 DLL,但對於任何重新開始的人,我不會推薦這種方法。 此外,Unity 沒有進一步開發(據我所知),因此我建議將 Autofac 框架用於新項目。 有關如何執行此操作的更多信息,請參閱此帖子

介紹
我正在使用 ASP.NET 和 MVC 構建一個 Web 應用程序。 此應用程序依賴於某些服務(WCF 服務、數據存儲服務等)。 現在為了保持良好和解耦,我想使用 DI(依賴注入)框架,特別是 Unity。

初步研究
我找到了這篇博文,但遺憾的是它不起作用。 不過這個主意不錯。
它基本上是說您不應該將在 ServiceCollection 中注冊的所有服務注冊到您自己的容器中,而是引用默認的 ServiceProvider。
所以。 如果需要解決某些問題,則調用默認的 ServiceProvider,如果它沒有解決方案,則將使用您的自定義 UnityContainer 來解決該類型。

問題
MVC 總是嘗試使用默認的 ServiceProvider 解析控制器。
此外,我注意到即使控制器能夠正確解析,我也永遠無法“混合”依賴項。 現在,如果我想使用我的服務之一以及來自 ASP 的 IOptions 接口,則永遠無法解析該類,因為這兩個容器中沒有一個具有這兩種類型的解析。

我需要什么
所以回顧一下,我需要以下幾點:

  • 我不需要將 ASP.NET 依賴項復制到我的 UnityContainer 中的設置
  • 一個可以解析我的 MVC 控制器的容器
  • 一個可以解決“混合”依賴關系的容器

編輯:
所以問題是我怎樣才能達到這些點?

環境
項目.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

    /// <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 部分。

解析控制器
我找到了這篇博文 從中我了解到 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
}

現在,如果控制器類被激活,它將使用我的 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 注冊我的控制器類,因為默認的 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 如果是這樣請告訴我!

對於 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();

是 ASP.Net Core 的 Unity DI 示例。

我在我的 ASP.Net Core 應用程序中使用了這個解決方案並且運行良好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM