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; }

它允许你有多个接口的实现类,如果你有多个构造函数的需求,完全可以一个接口写多个实现类去做,通过导入不同的实现类去代替不同的构造函数的用法。也不知道园友有没有更好的解决方案?不吝赐教~~

时间: 2024-10-25 22:14:55

C#进阶系列——MEF实现设计上的“松耦合”(四):构造函数注入的相关文章

C#进阶系列——MEF实现设计上的“松耦合”(二)

前言:前篇 C#进阶系列——MEF实现设计上的“松耦合”(一) 介绍了下MEF的基础用法,让我们对MEF有了一个抽象的认识.当然MEF的用法可能不限于此,比如MEF的目录服务.目录筛选.重组部件等高级应用在这里就不做过多讲解,因为博主觉得这些用法只有在某些特定的环境下面才会用到,着实不太普遍,感觉没有钻下去的必要.如果你有兴趣也可以去了解下.这篇打算将MEF和仓储模式结合起来谈谈MEF在项目中的使用. 1.仓储模式:也叫Repository模式.Repository是一个独立的层,介于领域层与数

C#进阶系列——MEF实现设计上的“松耦合”(一)

前言:最近去了趟外地出差,介绍推广小组开发的框架类产品.推广对象是本部门在项目上面的同事——1到2年工作经验的初级程序员.在给他们介绍框架时发现很多框架设计层面的知识他们都没有接触过,甚至没听说过,这下囧了~~于是乎在想该如何跟他们解释MEF.AOP.仓储模式等方面的东东.本来 C#基础系列 应该还有两篇关于异步的没有写完,奈何现在要推广这些个东西,博主打算先介绍下项目中目前用到的些技术,异步的往后有时间再做分享.C#进阶系列主要围绕MEF.AOP.仓储模式.Automapper.WCF等展开.

MEF实现设计上的“松耦合”

C#进阶系列——MEF实现设计上的“松耦合”(二) 前言:前篇 C#进阶系列——MEF实现设计上的“松耦合”(一) 介绍了下MEF的基础用法,让我们对MEF有了一个抽象的认识.当然MEF的用法可能不限于此,比如MEF的目录服务.目录筛选.重组部件等高级应用在这里就不做过多讲解,因为博主觉得这些用法只有在某些特定的环境下面才会用到,着实不太普遍,感觉没有钻下去的必要.如果你有兴趣也可以去了解下.这篇打算将MEF和仓储模式结合起来谈谈MEF在项目中的使用. 1.仓储模式:也叫Repository模式

MEF实现设计上的“松耦合”(一)

1.什么是MEF 先来看msdn上面的解释:MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库. 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置. 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项. 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展. 也有人把MEF解释为“依赖注入”的一种方式,那么什么是“依赖注入”?如果这样解释,感觉越陷越深......根据博主的理解,了解MEF只需要

MEF实现设计上的“松耦合”(二)

介绍了下MEF的基础用法,让我们对MEF有了一个抽象的认识.当然MEF的用法可能不限于此,比如MEF的目录服务.目录筛选.重组部件等高级应用在这里就不做过多讲解,因为博主觉得这些用法只有在某些特定的环境下面才会用到,着实不太普遍,感觉没有钻下去的必要.如果你有兴趣也可以去了解下.这篇打算将MEF和仓储模式结合起来谈谈MEF在项目中的使用. 1.仓储模式:也叫Repository模式.Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间.它的存在让领域层感觉不到数据访问层的

MEF实现设计上的“松耦合”(三)

1.面向接口编程:有一定编程经验的博友应该都熟悉或者了解这种编程思想,层和层之间通过接口依赖,下层不是直接给上层提供服务,而是定义一组接口供上层调用.至于具体的业务实现,那是开发中需要做的事情,在项目架构阶段,只需要定义好层与层之间的接口依赖,将框架搭起来,编译可以直接通过.为什么要有这么一种设计?既然是架构设计,当然是为了提高架构的灵活性,降低层和层之间的依赖(耦合).这个并非一句两句讲得清楚的,更多详细可以参看:面向接口编程详解(一)——思想基础.此文我觉得分析比较到位.好了,不说废话,来看

JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象

本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = { firstName: "Darren", lastName: "Ji", getFullName: function() { return this.firstName + " " + this.lastName; } }; 如果需要创建2个结

C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原因无非以下两点:一是Repository的真实意图没有理解清楚,导致设计的紊乱,随着项目的横向和纵向扩展,到最后越来越难维护:二是赶时髦的为了“模式”而“模式”,仓储并非适用于所有项目,这就像没有任何一种架构能解决所有的设计难题一样.本篇通过这个设计的Demo来谈谈博主对仓储的理解,有不对的地方还望

JavaScript进阶系列06,事件委托

在"JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数"中已经有了一个跨浏览器的事件处理机制.现在需要使用这个事件处理机制为页面元素注册事件方法. □ 点击页面任何部分触发事件 创建一个script1.js文件. (function() { eventUtility.addEvent(document, "click", function(evt) { alert('hello'); }); }(