前言:今天十一長假的第一天,本因出去走走,奈何博主最大的樂趣是假期坐在電腦前看各處堵車,順便寫寫博客,有點收獲也是好的。關於MEF的知識,之前已經分享過三篇,為什么有今天這篇?是因為昨天分享領域服務的時候,用到MEF的注入有參構造函數的方法,博主好奇心重,打算稍微深挖一下,這篇來對此知識點做個總結。
還是將前面三篇的目錄列出來,對MEF沒有了解的朋友,可以先看看:
一、知識點回顧
我們知道MEF作為IOC的方式之一,它的主要作用是解耦,MEF加上面向接口編程,可以使得你的設計更加靈活。我們知道類的構造函數是可以重載的,我們通過構造函數可以向對象傳遞參數。那么如果我們的MEF也需要通過構造函數傳參怎么辦呢?別擔心,有我們神奇的ImportingConstructor為您解決。
二、代碼示例
作為分享代碼,博主還是打算用前面DDD里面領域服務用到的那個Demo,現學現賣嘛,O(∩_∩)O~
1、准備代碼:
作為MEF的導入導出的對象,我們先來看三個倉儲接口和實現
public interface IUserRepository:IRepository<TB_USERS> { IEnumerable<TB_USERS> GetUsersByRole(TB_ROLE oRole); }
[Export(typeof(IUserRepository))] public class UserRepository:EFBaseRepository<TB_USERS>,IUserRepository { public IEnumerable<TB_USERS> GetUsersByRole(TB_ROLE oRole) { throw new NotImplementedException(); } }
public interface IRoleRepository:IRepository<TB_ROLE> { }
[Export(typeof(IRoleRepository))] public class RoleRepository:EFBaseRepository<TB_ROLE>,IRoleRepository { }
public interface IUserRoleRepository : IRepository<TB_USERROLE> { }
[Export(typeof(IUserRoleRepository))] public class UserRoleRepository : EFBaseRepository<TB_USERROLE>, IUserRoleRepository { }
2、構造函數傳入單個參數
直接來看代碼吧:
[Export(typeof(IPowerManagerDomainService))] public class PowerManagerDomainService:IPowerManagerDomainService { private IUserRepository _userRepository = null; private IRoleRepository _roleRepository = null; private IUserRoleRepository _userroleRepository = null; [ImportingConstructor] public PowerManagerDomainService(IUserRoleRepository oUserRoleRepository) { _userroleRepository = oUserRoleRepository; } }
為什么通過這里的ImportingConstructor特性就能將參數IUserRoleRepository oUserRoleRepository順利傳進來?還記得前面的准備代碼嗎,IUserRoleRepository的實現類UserRoleRepository上面標記過導出[Export(typeof(IUserRepository))],所以這里能將參數順利導入進來。還是來看看調用代碼:
[Import] public IPowerManagerDomainService powerDomainService { get; set; } static void Main(string[] args) { var oProgram = new Program(); Regisgter.regisgter().ComposeParts(oProgram); Console.ReadKey(); }
來調試代碼看看:
3、構造函數傳入多個參數
其實多個參數和上面單個參數的的也沒啥太大區別
[ImportingConstructor] public PowerManagerDomainService(IUserRepository oUserRepository, IRoleRepository oRoleRepository) { _userRepository = oUserRepository; _roleRepository = oRoleRepository; }
同樣要求IUserRepository 和 IRoleRepository 類型要有對應的Export。
4、構造函數參數有多個導出
上面的例子都是默認倉儲的接口類型都只有一個導出的情況,當實際項目中,業務邏輯較復雜的時候,某一個接口往往存在多個實現類的導出,這種情況下我們要怎么辦呢?比如IUserRepository倉儲接口有兩個實現類:
[Export("userRepository_A", typeof(IUserRepository))] public class UserRepository_A:EFBaseRepository<TB_USERS>,IUserRepository { public IEnumerable<TB_USERS> GetUsersByRole(TB_ROLE oRole) { throw new NotImplementedException(); } }
[Export("userRepository_B", typeof(IUserRepository))] public class UserRepository_B:EFBaseRepository<TB_USERS>,IUserRepository { public IEnumerable<TB_USERS> GetUsersByRole(TB_ROLE oRole) { throw new NotImplementedException(); } }
這種情況下,如果我們直接在構造函數里面這樣寫
[ImportingConstructor] public PowerManagerDomainService(IUserRepository oUserRepository, IRoleRepository oRoleRepository) { _userRepository = oUserRepository; _roleRepository = oRoleRepository; }
肯定是會報錯的。那么我們的解決方案是:
[ImportingConstructor] public PowerManagerDomainService([Import("userRepository_A", typeof(IUserRepository))]IUserRepository oUserRepository, IRoleRepository oRoleRepository) { _userRepository = oUserRepository; _roleRepository = oRoleRepository; }
5、多個構造函數的導入
了解了上面那么多,我們還想擴展一下,我們知道構造函數是可以重載的,一個類可以有多個構造函數。那么如果我們想在多個構造函數上面同時標記ImportingConstructor特性,然后根據需要調用不同的構造函數,這樣真的行嗎?比如我們想這樣寫:
[Export(typeof(IPowerManagerDomainService))] public class PowerManagerDomainService:IPowerManagerDomainService { private IUserRepository _userRepository = null; private IRoleRepository _roleRepository = null; private IUserRoleRepository _userroleRepository = null; [ImportingConstructor] public PowerManagerDomainService(IUserRoleRepository oUserRoleRepository) { _userroleRepository = oUserRoleRepository; } [ImportingConstructor] public PowerManagerDomainService([Import(typeof(IUserRepository))]IUserRepository oUserRepository, IRoleRepository oRoleRepository) { _userRepository = oUserRepository; _roleRepository = oRoleRepository; } }
到底行不行呢?我們來測一把:
願望是美好的,但異常是殘酷的!看異常的具體信息:因為未能選擇構造函數進行構造。請確保該類型具有默認構造函數或有一個標記有“System.ComponentModel.Composition.ImportingConstructorAttribute”的構造函數。很顯然MEF的ImportingConstructorAttribute特性不支持這種多個構造函數同時標注的情況。將ImportingConstructorAttribute轉到定義,發現它也沒有其他可用屬性
難道是博主的需求太奇葩啦?苦思良久,仍未找到解決方案。后來博主仔細想了想,可能是側重點的問題,MEF一般情況是和面向接口編程聯系起來用的,也就是說正常情況下我們定義的是一個接口類型的變量,例如:
[Import] public IPowerManagerDomainService powerDomainService { get; set; }
它允許你有多個接口的實現類,如果你有多個構造函數的需求,完全可以一個接口寫多個實現類去做,通過導入不同的實現類去代替不同的構造函數的用法。也不知道園友有沒有更好的解決方案?不吝賜教~~