三.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");


        }
    }
}

results matching ""

    No results matching ""