January 27, 2013

ASP.NET MVC 4中使用StructureMap

筆者在IoC container的選擇上一直是使用StructureMap,其實也不是有什麼偏好,只是當時初入門Denpendency Injection這議題時,看的便是使用StructureMap。一般IoC container在設定上不外乎在Web.config、App.config或是IoC framework自己的設定檔裡設定相依,如
<?xml version="1.0" encoding="utf-8" ?>
<StructureMap MementoStyle="Attribute">
  <DefaultInstance
    PluginType="Wais.Service.IRegisterService,Wais.Service"
    PluggedType="Wais.Service.RegisterService,Wais.Service"
    Scope="Singleton" />
  <DefaultInstance
    PluginType="Wais.Domain.IUserAccountRepository,Wais.Domain"
    PluggedType="Wais.Infrastructure.Data.EF.UserAccountRepository,Wais.Infrastructure.Data.EF"
    Scope="Singleton" />
  <DefaultInstance
    PluginType="Wais.Infrastructure.IUnitOfWork,Wais.Infrastructure"
    PluggedType="Wais.Infrastructure.Data.EF.WaisEntities,Wais.Infrastructure.Data.EF"
    Scope="Singleton" />
</StructureMap>
或是直接將container的設定寫在Global.asax的Application_Start裡如
ObjectFactory.Initialize(x =>
            {
                x.For<IUnitOfWork>().Use<WaisEntities>();
                x.For<IUserAccountRepository>().Use<UserAccountRepository>();
                x.For<IForgotPasswordRepository>().Use<ForgotPasswordRepository>();
                x.For<IRegisterService>().Use<RegisterService>();
                x.For<IUserAccountService>().Use<UserAccountService>();
                x.For<IForgotPasswordService>().Use<ForgotPasswordService>();
            });
將container設定寫在程式裡好處是有intellisense的支援,不過就必須把相依的實作也加入專案中,如此Web Application專案也會相依Data Access Layer或是Repository的實作專案,但一般我們會避免這種情況發生以免開發人員誤用。理想的狀況應該只相依介面專案,如Service Layer的介面專案。在這情況下使用設定檔方式設定IoC container即可免除上述困擾,但壞處就是要謹慎一點,有時打錯個字找半天都找不到問題在哪裡,實作類別更名時也得記得回來修改。

還有一種做法是將IoC container的設定全放在另一個專案中,例如筆者有個函式庫專案名稱叫Wais.Web.DependencyInjection,裡面載入Service Layer及Repoistory所用到的介面及實作。而筆者的Web Application專案叫Wais.Web則加入Wais.Web.DependencyInjection為參考,如此只要在Application_Start中呼叫Wais.Web.DependencyInjection專案中某支方法便可初始化IoC container。

不過在ASP.NET MVC 4中如果要使用StructureMap在設定上有點小麻煩,為了要能初始化Controller,還需額外實作IDependencyResolver介面,在此我們不多談,直接使用nuget上所提供的StructureMap.MVC4。安裝完後會在專案中建立App_StartDependencyResolution資料夾。App_Start下會有一個StructuremapMvc類別,而DependencyResolution下會有IoC、StructureMapDependencyResolver及StructureMapDependencyScope三個類別。我們只要到IoC這個類別中設定dependency即可,完全不需要在Global.asax做任何程式上的修改,StructureMap.MVC4即會透過WebActivator(StructuremapMvc這支類別)於應用程式啟動時自動載入。
using StructureMap;
using Wais.Domain;
using Wais.Infrastructure;
using Wais.Infrastructure.Data.EF;
using Wais.Service;

namespace Wais.Web.DependencyInjection.DependencyResolution
{
    public static class IoC
    {
        public static IContainer Initialize()
        {
            ObjectFactory.Initialize(x =>
                        {
                            x.For<IUnitOfWork>().Use<WaisEntities>();
                            x.For<IUserAccountRepository>().Use<UserAccountRepository>();
                            x.For<IForgotPasswordRepository>().Use<ForgotPasswordRepository>();
                            x.For<IRegisterService>().Use<RegisterService>();
                            x.For<IUserAccountService>().Use<UserAccountService>();
                            x.For<IForgotPasswordService>().Use<ForgotPasswordService>();
                        });
            return ObjectFactory.Container;
        }
    }
}
而在Controller或是Service做Constructor Injection時,StructureMap就會自動對應到相依的實作類別了,相當方便。
private IRegisterService _registerService;
private IUserAccountService _userAccountService;

public HomeController(IRegisterService registerService, IUserAccountService userAccountService)
{
    this._registerService = registerService;
    this._userAccountService = userAccountService;
}
private IUserAccountRepository _userAccountRepository;

public RegisterService(IUserAccountRepository userAccountRepository)
{
    if (userAccountRepository == null)
    {
        throw new ArgumentNullException("userAccountRepository");
    }

    this._userAccountRepository = userAccountRepository;
}
相關文章

No comments: