所以經過一番研究,我想出了以下解決方案,以我的問題:
使用統一與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
}
此外,我不得不從這個在我的啓動類改變ConfigureServices方法的簽名:
public void ConfigureServices(IServiceCollection services)
這樣:
public IServiceProvider ConfigureServices(IServiceCollection services)
現在我可以回到我的自定義的IServiceProvider和它將被使用,而不是默認的。
完整的ConfigureServices方法顯示在底部的接線部分。
解析控制器
我找到了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
}
現在,如果一個控制器類被激活,它會與我UnityContainer被instatiated。因此,我的UnityContainer必須知道如何解決任何控制器!
下一個問題:使用默認的IServiceProvider
現在,如果我註冊服務,例如在的mvc ASP.NET我通常會做這樣的:如果我使用一個UnityContainer所有MVC
services.AddMvc();
現在無法解決依賴關係,因爲它們沒有註冊。所以我可以註冊它們(比如AutoFac)或者我可以創建一個UnityContainerExtension。我選擇了擴展,並與以下兩個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沒有註冊的東西,它只是要求默認的提供者。
我瞭解到這一切從幾個不同的文章
這種方法的好處是,我也可以 「混合」 的依賴了。如果我需要我的任何服務和一個來自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;
}
因爲我學到最多的就是我在過去的一週,我希望我沒有違反任何大Pricipal /模式如果是的話,請告訴我知道DI!
不是真的問題 – RollRoll
現在好了嗎?如果不是,我應該如何重述它? –