三.MEF
作者的文章內未將Unity改為MEF的方式,其原因在於ProductService的建構式注入還是使用類別:
UnitOfWork
而非介面:IUnitOfWork
。
UnitOfWork
未使用RegesertTYpe卻還能跑的主要原因在於ProductService被註冊進Unity的Reslolve後,因為不像Interface會有多重相依性的問題,只要是實體類別的都會幫你自動new一個實體物件。建構式注入可參考:Annotating Objects for Constructor Injection.aspx)
MEF 提供方法,透過「組合」(composition) 隱含地找出可用的元件(dll),最常見的做法就是去指定資料夾路徑,動態讀取元件(dll)。
介紹
好處
- 封裝
- 降低相依性
- 提高重用性
注意
- .Net Framework 4.0才支援
開始撰寫MEF相關功能
建立專案
名稱:Resolver 類型:類別庫
nuget
加入 Utilty
組件
加入 System.ComponentModel.Composition
建立介面
IComponent.cs
IRegisterComponent隨後會建立。
namespace Resolver
{
/// <summary>
/// Register underlying types with unity.
/// </summary>
public interface IComponent
{
void SetUp(IRegisterComponent registerComponent);
}
}
IRegisterComponent
定義幾個方法可透過IComponent在Untilty的Config註冊Type。
Untilty註冊類型的其中之一個方式:container.RegisterType
namespace Resolver
{
/// <summary>
/// Responsible for registering types in unity configuration by implementing IComponent
/// </summary>
public interface IRegisterComponent
{
/// <summary>
/// Register type method
/// </summary>
/// <typeparam name="TFrom"></typeparam>
/// <typeparam name="TTo"></typeparam>
/// <param name="withInterception"></param>
void RegisterType<TFrom, TTo>(bool withInterception = false) where TTo : TFrom;
/// <summary>
/// Register type with container controlled life time manager
/// </summary>
/// <typeparam name="TFrom"></typeparam>
/// <typeparam name="TTo"></typeparam>
/// <param name="withInterception"></param>
void RegisterTypeWithControlledLifeTime<TFrom, TTo>(bool withInterception = false) where TTo : TFrom;
}
}
實作方法
ComponentLoader.cs
using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Resolver
{
public static class ComponentLoader
{
public static void LoadContainer(IUnityContainer container, string path, string pattern)
{
var dirCat = new DirectoryCatalog(path, pattern);
var importDef = BuildImportDefinition();
try
{
using (var aggregateCatalog = new AggregateCatalog())
{
aggregateCatalog.Catalogs.Add(dirCat);
using (var componsitionContainer = new CompositionContainer(aggregateCatalog))
{
IEnumerable<Export> exports = componsitionContainer.GetExports(importDef);
IEnumerable<IComponent> modules =
exports.Select(export => export.Value as IComponent).Where(m => m != null);
var registerComponent = new RegisterComponent(container);
foreach (IComponent module in modules)
{
module.SetUp(registerComponent);
}
}
}
}
catch (ReflectionTypeLoadException typeLoadException)
{
var builder = new StringBuilder();
foreach (Exception loaderException in typeLoadException.LoaderExceptions)
{
builder.AppendFormat("{0}\n", loaderException.Message);
}
throw new TypeLoadException(builder.ToString(), typeLoadException);
}
}
private static ImportDefinition BuildImportDefinition()
{
return new ImportDefinition(
def => true, typeof(IComponent).FullName, ImportCardinality.ZeroOrMore, false, false);
}
}
internal class RegisterComponent : IRegisterComponent
{
private readonly IUnityContainer _container;
public RegisterComponent(IUnityContainer container)
{
this._container = container;
//Register interception behaviour if any
}
public void RegisterType<TFrom, TTo>(bool withInterception = false) where TTo : TFrom
{
if (withInterception)
{
//register with interception
}
else
{
this._container.RegisterType<TFrom, TTo>();
}
}
public void RegisterTypeWithControlledLifeTime<TFrom, TTo>(bool withInterception = false) where TTo : TFrom
{
this._container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
}
}
}
降低BusinessServices專案的相依性
組件
- 加入System.ComponentModel.Composition
專案參考
- Resolver
實作
DependencyResolver.cs
透過反射匯入IComponent的Type
using System.ComponentModel.Composition;
using DataModel;
using DataModel.UnitOfWork;
using Resolver;
namespace BusinessServices
{
[Export(typeof(IComponent))]
public class DependencyResolver : IComponent
{
public void SetUp(IRegisterComponent registerComponent)
{
registerComponent.RegisterType<IProductServices, ProductServices>();
registerComponent.RegisterType<IUnitOfWork, UnitOfWork>();
}
}
}
降低DataModel專案的相依性
組件
- 加入System.ComponentModel.Composition
專案參考
- Resolver
實作方法
建立UnitOfWork,放置下列檔案,並修改UnitOfWork.cs
的namespace:
- IUnitOfWork.cs
- UnitOfWork.cs
IUnitOfWork.cs
namespace DataModel.UnitOfWork
{
public interface IUnitOfWork
{
GenericRepository<Products> ProductRepository { get; }
/// <summary>
/// Save method.
/// </summary>
void Save();
}
}
UnitOfWork.cs
- 將UnitOfWork繼承IUnitOfWork
- 建構式及物件的unitofWork的類型由類別(UnitOfWork)改為介面(IUnitOfWork)
namespace DataModel.UnitOfWork
{
/// <summary>
/// Unit of Work class responsible for DB transactions
/// </summary>
public class UnitOfWork : IDisposable, IUnitOfWork
{
/******** 修改前的版本 *****************
private readonly UnitOfWork _unitOfWork;
/// <summary>
/// Public constructor.
/// </summary>
public ProductServices(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
**************************************/
//改為介面
private readonly IUnitOfWork _unitOfWork;
/// <summary>
/// Public constructor.
/// </summary>
public ProductServices(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
....以下省略
}
}
REST endpoint / WebAPI project
nuget
- 加入Unity.Mvc5
組件
- 移除 DataModel專案的參考
開始實作
UnityConfig.cs
改為MEF的註冊方式:
- BuildUnityContainer的註冊方式改為MEF
using Microsoft.Practices.Unity;
using Resolver;
using System.Web.Http;
using Unity.WebApi;
namespace WebApi
{
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = BuildUnityContainer();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
/***** 原本Untiy的註冊方式 *********
container
.RegisterType<IProductServices, ProductServices>()
.RegisterType<UnitOfWork>(new HierarchicalLifetimeManager());
********************************/
//將註冊改為MEF的方式
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
//Component initialization via MEF
ComponentLoader.LoadContainer(container, ".\\bin", "BusinessServices.dll");
}
}
}