C#進階系列——MEF實現設計上的“松耦合”(四):構造函數注入


前言:今天十一長假的第一天,本因出去走走,奈何博主最大的樂趣是假期坐在電腦前看各處堵車,順便寫寫博客,有點收獲也是好的。關於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; }

它允許你有多個接口的實現類,如果你有多個構造函數的需求,完全可以一個接口寫多個實現類去做,通過導入不同的實現類去代替不同的構造函數的用法。也不知道園友有沒有更好的解決方案?不吝賜教~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM