Code Copied

Unity系列教程(三)Registration by Convention

Convention over Configuration

Convention over Configuration翻译为中文是惯例优先原则(也可称为按照惯例编码),是时下较为流行的一种软件设计模式。
该模式要求开发人员按照命名惯例进行开发工作,开发人员无需再耗费大量的经历在繁杂的配置上,转而花费精力到非惯例的开发上。
这种设计模式不仅能够大大减少类似的重复配置工作,还能够约束开发人员的编码规范,而且还不失灵活性。
所谓惯例,就是一些命名上的约定。下面的几个例子都采用了惯例优先原则:
例1:ASP.NET MVC的Controller命名必须带有后缀Controller,Controller对应的View文件夹命名也源自Controller的名称。
例2:在使用Entity Framework作为数据访问框架的程序中,如果命名了Product实体类,那么对应的数据库表则是dbo.Products。如果需要将表命名为dbo.t_Products,开发人员才需要写有关这个名字的配置。

Configuration by Coding

在实现Repository层时,一般我们会约定IxxxRepository接口以及对应的xxxRepository实现(xxx是Model Class的名称)。

public interface IProductRepository
{
    
}

public class ProductRepository : IProductRepository
{

}

在使用Unity的Container注册接口和实现的mapping关系时,需要在UnityConfig中使用下面的方式配置:

container.RegisterType<IProductRepository, ProductRepository>();

一个完整的企业级应用系统设计开发下来,可能会有数百上千的这种mapping关系,而且他们的配置代码如出一辙。
按照这种配置方式,将会使配置类或者文件越来越臃肿,而且难于管理和维护。

Registration by Convention

幸运的是,从Unity 3.0开始已经支持Registration by Convention(按照约定的自动注册机制)。

RegisterTypes方法

在Unity的Microsoft.Practices.Unity.RegistrationByConvention组件中,有一个UnityContainer的扩展方法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, the MatchingInterface property creates mappings where there are interfaces and implementations that follow the naming convention ITenant 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.

Convention支持的规则

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

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

简单示例

现在Repositories层的接口和类正在逐渐增多,我们想通过Registration by Convention的方式自动注册Repositories层的所有接口和类的mapping关系。

image

在ContainerBootstrapper类中使用RegisterTypes方法

1. 使用AllClasses.FromAssembliesInBasePath()

public class ContainerBootstrapper
{
    public static void RegisterTypes(IUnityContainer container)
    {
        Trace.WriteLine(string.Format("Called RegisterTypes in ContainerBootstrapper"), "UNITY");

        container.RegisterTypes(
            AllClasses.FromAssembliesInBasePath(),
            WithMappings.FromMatchingInterface,
            WithName.Default,
            WithLifetime.ContainerControlled);
    }
}

输出结果:

image

2. 使用AllClasses.FromLoadedAssemblies()

container.RegisterTypes(
                AllClasses.FromLoadedAssemblies(),
                WithMappings.FromMatchingInterface,
                WithName.Default,
                WithLifetime.ContainerControlled);

image

3. 使用Linq过滤namespace

上面的2个默认示例调用中,将不需要注册的接口和类也注册了,通过linq指定namespace可以注册我们期望注册的类和接口。

container.RegisterTypes(
                AllClasses.FromLoadedAssemblies().Where(n => n.Namespace == "RegisterByConventionConsoleApp.Repositories"),
                WithMappings.FromMatchingInterface,
                WithName.Default,
                WithLifetime.ContainerControlled);

运行结果:

image

4. 自定义约定类

可以通过继承抽象类RegistrationConvention来扩展我们其他需求的约定。

public class CustomizedRegistration : 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()
        {
            // here specify the types you want to register
            // in this example, we just register the ProductRepository and IProductRepository
            yield return typeof(ProductRepository);
        }
    }

调用自定义Convention:

container.RegisterTypes(new CustomizedRegistration());

运行结果:

image

参考

http://en.wikipedia.org/wiki/Convention_over_configuration

http://blogs.msdn.com/b/agile/archive/2013/03/12/unity-configuration-registration-by-convention.aspx

http://www.cnblogs.com/gaochundong/p/unity_3_registration_by_convention.html