前文《Unity2.0容器自动注册机制》中,介绍了如何在 Unity 2.0 版本中使用 Auto Registration 自动注册机制。在 Unity 3.0 版本中(2013年),新增了基于约定的自动注册机制(Registration By Convention),以使 Unity 容器的装配过程变得更加简单,并且减少冗余代码。

Convention over Configuration

Convention over Configuration 是现如今非常流行的设计风格,很多框架都在尝试采纳该风格,包括 ASP.NET MVC。简要的说就是需要依赖某种预先定义的约定,类似于文件、目录的命名,类或者系统依据约定做指定的事等。例如,在 ASP.NET MVC 中,所有 Controller 都必须包含 “Controller” 后缀,这样框架才能找到该 Controller 并进行初始化。

约定风格使开发人员的生活变得轻松多了,我们无需再耗费精力在繁杂的配置上,转而采用一些简单的约定规则。

例如,有一个时常出现的模式。我们定义一个类 Foo 来作为接口 IFoo 的默认实现。在匈牙利命名法的编程风格中,标记 “I” 通常是接口的第一个字母。利用这一点,我们可以设定一个命名约定。

   public interface IConvention
{
} public class Convention : IConvention
{
}

基于此约定,我们可以扫描整个程序集来查找满足约定的接口和类,并自动注册进 Unity 容器。

Registration by Convention

早在2009年,Jeremy Miller 在 StructureMap 2.5 版本中即已引入了基于约定的自动注册机制,通过拥抱 Convention over Configuration 设计范式来降低使用 Configuration 的开销。

同年,Derek Greer 为 Unity 2.0 增加了 Convention-based Registration Extension 扩展。而微软官方直到2013年 Unity 3.0 才增加此功能。

基于约定的自动注册机制目的在于简化类型注册过程,减少类型注册代码。比起无数的 “container.RegisterType<IFoo, Foo>()”,可以直接通过扫描程序集,依据一些既定的规则来自动完成类型注册。

       using (var container = new UnityContainer())
{
container.RegisterTypes(
AllClasses.FromAssembliesInBasePath(),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
}

支持的 Conventions

Convention 必须指明哪些可用类型应该被映射进 Unity 容器中。当前的 Unity 3.0 中的可用约定支持如下规则:

  1. 识别扫描被注册类型所在的程序集:可以使用提供的程序集列表,使用当前已加载的程序集列表,使用在当前应用程序目录中的所有程序集列表。可以进一步通过类型的名称、后缀或者其他规则进行过滤,并且支持 LINQ 语法。这一步准备了可能被注册的类型列表。默认情况下,所有系统程序集将被自动过滤掉。当然,也不是非要使用帮助类中所提供的类型加载方式,可以使用一个方法来获取类型的枚举集合,或者甚至直接使用一个数组。
  2. 可选项,指定哪些类型(典型的是接口或者抽象类)需要通过容器来映射到步骤 1 中的类型。目前的约定包括:
    • 映射哪些遵循命名约定的类型。例如类 Foo 和 接口 IFoo 。
    • 映射类型实现的所有接口。
    • 映射类型实现的所有接口,并且这些接口必须与类型定义在同一程序集内。
  3. 可选项,指定是否使用命名注册(Named Registration)。也可以使用类型的名称作为注册名称。
  4. 可选项,指定注册类型时使用哪种生命周期管理器(Lifetime Manager)。
  5. 可选项,确定是否有类型成员需要注入(Injected Parameter)。

所有约定通过 RegisterTypes 方法来表述:

 public static IUnityContainer RegisterTypes(
this IUnityContainer container,
IEnumerable<Type> types,
Func<Type, IEnumerable<Type>> getFromTypes = null,
Func<Type, string> getName = null,
Func<Type, LifetimeManager> getLifetimeManager = null,
Func<Type, IEnumerable<InjectionMember>> getInjectionMembers = null,
bool overwriteExistingMappings = false);

RegisterTypes 方法签名描述:

Parameter

Description

Types

This parameter is an enumerable collection of types that you want to register with the container. These are the types that you want to register directly or create mappings to. You can create this collection by providing a list of types directly or by using one of the methods of the built-in AllClasses helper class: for example, the method FromLoadedAssemblies loads all of the available types from the currently loaded assemblies.

You can use LINQ to filter this enumeration.

getFromTypes

This optional parameter identifies the types you want to map from in the container. The built-in WithMappings helper class provides several options for this mapping strategy: for example, theMatchingInterface property creates mappings where there are interfaces and implementations that follow the naming conventionITenant and Tenant.

getName

This optional parameter enables you to control whether to create default registrations or named registrations for the types. The built-in helper class WithName, enables you to choose between using default registrations or named registrations that use the type name.

getLifeTimeManager

This optional parameter enables you to select from the built-in lifetime managers.

getInjectionMembers

This optional parameter enables you to provide definitions for any injection members for the types that you are registering.

overwriteExistingMappings

This optional parameter enables you to control how the method behaves if it detects an attempt to overwrite an existing mapping in the Unity container. By default, the RegisterTypes method throws an exception if it detects such an attempt. If this parameter is true, the method silently overwrites an existing mapping with a new one based on the values of the other parameters.

简单示例:

       var container = new UnityContainer();

       container.AddNewExtension<Interception>();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(
t => t.Namespace == "OtherUnitySamples"),
WithMappings.FromMatchingInterface,
getInjectionMembers: t => new InjectionMember[]
{
new Interceptor<VirtualMethodInterceptor>(),
new InterceptionBehavior<LoggingInterceptionBehavior>()
});

自定义约定类

Unity 通过提供注册约定抽象类 RegistrationConvention 来提供扩展性。

   public abstract class RegistrationConvention
{
public abstract Func<Type, IEnumerable<Type>> GetFromTypes();
public abstract Func<Type, IEnumerable<InjectionMember>> GetInjectionMembers();
public abstract Func<Type, LifetimeManager> GetLifetimeManager();
public abstract Func<Type, string> GetName();
public abstract IEnumerable<Type> GetTypes();
}

我们可以自定义实现 RegistrationConvention :

   class CustomizedConvention : RegistrationConvention
{
public override Func<Type, IEnumerable<Type>> GetFromTypes()
{
return t => t.GetInterfaces();
} public override Func<Type, IEnumerable<InjectionMember>> GetInjectionMembers()
{
return null;
} public override Func<Type, LifetimeManager> GetLifetimeManager()
{
return t => new ContainerControlledLifetimeManager();
} public override Func<Type, string> GetName()
{
return t => t.Name;
} public override IEnumerable<Type> GetTypes()
{
yield return typeof(Foo);
yield return typeof(Bar);
}
}
       var container = new UnityContainer();
container.RegisterTypes(new CustomizedConvention());

显示已注册类型信息

可以通过显示枚举容器的 Registrations 属性来获得所有的注册信息。

   class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterTypes(new CustomizedConvention()); Console.WriteLine("Container has {0} Registrations:",
container.Registrations.Count());
foreach (ContainerRegistration item in container.Registrations)
{
Console.WriteLine(item.GetMappingAsString());
} Console.ReadKey();
}
} public interface IFoo { }
public class Foo : IFoo { }
public interface IBar { }
public class Bar : IBar { } static class ContainerRegistrationsExtension
{
public static string GetMappingAsString(
this ContainerRegistration registration)
{
string regName, regType, mapTo, lifetime; var r = registration.RegisteredType;
regType = r.Name + GetGenericArgumentsList(r); var m = registration.MappedToType;
mapTo = m.Name + GetGenericArgumentsList(m); regName = registration.Name ?? "[default]"; lifetime = registration.LifetimeManagerType.Name;
if (mapTo != regType)
{
mapTo = " -> " + mapTo;
}
else
{
mapTo = string.Empty;
}
lifetime = lifetime.Substring(
, lifetime.Length - "LifetimeManager".Length);
return String.Format(
"+ {0}{1} '{2}' {3}", regType, mapTo, regName, lifetime);
} private static string GetGenericArgumentsList(Type type)
{
if (type.GetGenericArguments().Length == ) return string.Empty;
string arglist = string.Empty;
bool first = true;
foreach (Type t in type.GetGenericArguments())
{
arglist += first ? t.Name : ", " + t.Name;
first = false;
if (t.GetGenericArguments().Length > )
{
arglist += GetGenericArgumentsList(t);
}
}
return "<" + arglist + ">";
}
}

输出结果:

参考资料

Unity3.0基于约定的自动注册机制的更多相关文章

  1. 微软IOC容器Unity简单代码示例3-基于约定的自动注册机制

    @(编程) [TOC] Unity在3.0之后,支持基于约定的自动注册机制Registration By Convention,本文简单介绍如何配置. 1. 通过Nuget下载Unity 版本号如下: ...

  2. Unity2.0容器自动注册机制

    现如今可能每个人都会在项目中使用着某种 IoC 容器,并且我们的意识中已经形成一些固定的使用模式,有时会很难想象如果没有 IoC 容器工作该怎么进展. IoC 容器通过某种特定设计的配置,用于在运行时 ...

  3. My.Ioc 代码示例——实现自动注册/解析

    在很多 Ioc 容器中,当使用者向容器请求实现了某个契约类型 (Contract Type) 的服务时 (调用类似如下方法 container.Resolve(Type contractType)), ...

  4. Unity容器实现自动注册

    如何创建Unity容器? 首先NuGet搜索Unity, 该示例中使用的版本为4.0.1 新建控制台程序 示例中使用常规操作, 创建一个IPay接口, 分别有两个实现类: ApplePay.Huawe ...

  5. Ocelot + Consul + Registrator 基于Docker 实现服务发现、服务自动注册

    目录 1. Consul集群搭建 1.1 F&Q Consul官方推荐的host网络模式运行 2. Registrator服务注册工具 2.1 F&Q Registrator悬挂服务 ...

  6. thinkphp5 自动注册Hook机制钩子扩展

    Hook.php 文件已更新1.修复在linux环境下类的 \ 在basename 下无法获取到类名的问题2.修复linux 环境下无法使用hook::call 调用失败问题 请先安装thinkphp ...

  7. ThinkPHP5.0源码学习之注册自动加载

    ThinkPHP5框架的自动注册加载流程如下:

  8. 【tensorflow2.0】自动微分机制

    神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情. 而深度学习框架可以帮助我们自动地完成这种求梯度运算. Tensorflow一般使用梯度磁带tf.Gradi ...

  9. zabbix3.0.4 探索主机Discovery自动发现agent主机和zabbix-agent自动注册详细图文教程

    Zabbix 自动发现(Discovery)功能使用 随着监控主机不断增多,有的时候需要添加一批机器,特别是刚用zabbix的运维人员需要将公司的所有服务器添加到zabbix,如果使用传统办法去单个添 ...

随机推荐

  1. asp.net web api 测试帮助页面建立并测试

    asp.net web api 测试帮助页面建立并测试 现在使用WEB API来开发,越来越流行. 在开发过程中的测试调试,可以使用Fiddler等工具来帮助测试外,还有: 在asp.net 中有种方 ...

  2. 用Jenkins配置自动化构建

     公司培训内容 -------------->记一笔 dubbo 微服务soadiamond-server 配置中心kafka rocketmq消息队列cas-server 单点登录spring ...

  3. oracle 客户端重新安装遇到的问题

    前一阵,因为把ORACLE客户端的密码忘记了,在网上也找了好多方法,试着不重新安装找回密码,可是都行不通,没有办法重新装.安装时遇到一些问题,因为我是WIN7,64位系统,安装的oracle11g64 ...

  4. 开发中用到的开源框架汇总(Updating)

    SuperWebSocket SuperSocket NPOI 官网:http://npoi.codeplex.com/ 实例讲解:http://www.cnblogs.com/yutian/p/52 ...

  5. [2015hdu多校联赛补题]hdu5348 MZL's endless loop

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5348 题意:给你一个无向图,要你将无向图的边变成有向边,使得得到的图,出度和入度差的绝对值小于等于1, ...

  6. 驱动开发学习笔记. 0.01 配置arm-linux-gcc 交叉编译器

    驱动开发读书笔记. 0.01 配置arm-linux-gcc 交叉编译器 什么是gcc: 就像windows上的VS 工具,用来编译代码,具体请自己搜索相关资料 怎么用PC机的gcc 和 arm-li ...

  7. Ubuntu 编译安装 Linux 4.0.5 内核,并修复 vmware 网络内核模块编译错误

    . . . . . 今天把 Ubuntu 14.04 升级到了最新的 4.0.5 的内核版本,本来不打算记录下来的,但是升级的过程中确实遇到了一些问题,所以还是记录下来,分享给遇到同样问题的猿友. 先 ...

  8. Hdu OJ 5884-Sort (2016 ACM/ICPC Asia Regional Qingdao Online)(二分+优化哈夫曼)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5884 题目大意:有n个有序的序列,对于第i个序列有ai个元素. 现在有一个程序每次能够归并k个序列, ...

  9. Harris角点检测

    代码示例一: #include<opencv2/opencv.hpp> using namespace cv; int main(){ Mat src = imread(); imshow ...

  10. vim 分屏

    分屏启动Vim 使用大写的O参数来垂直分屏. vim -On file1 file2 ... 使用小写的o参数来水平分屏. vim -on file1 file2 ... 注释: n是数字,表示分成几 ...