.Net下的 IOC容器 UnityContainer的使用
前言
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
常见的IOC容器有 Autofac Spring.Net Castle
还有今天要介绍的Unity
github 地址为
https://github.com/unitycontainer/unity/tree/Announcements比较遗憾的是,由于缺少社区的支持,作者已经决定放弃它了。
.Net 5以及以上的项目就不要考虑它了。
一. 进入正题
项目要使用UnityContainer ,首先通过nuget包 引入到项目中
选择第一个。
其他Unity开头的都是Unity的一些扩展,提供了一些进阶的功能
引入Unity 命名空间后,即可打出Unity容器的类型了,先new一个
public static void Main(string[] args) { UnityContainer unityContainer = new UnityContainer(); }
准备好测试的类型,一个接口,两个实现
public interface ITest { void SayHi(); } public class Test1 : ITest { public void SayHi() { Console.WriteLine($"This is {GetType().Name}"); } } public class Test2 : ITest { public void SayHi() { Console.WriteLine($"This is {GetType().Name}"); } }
使用容器对Test1进行注册,并调用
得到ITest的实例为Test1
在这里已经做到了一个最基础的解耦了
调用端无需依赖细节,甚至连实例的类型都不知道,因为实例是由中间的第三方工厂来创建的。
public static void Main(string[] args) { UnityContainer unityContainer = new UnityContainer(); unityContainer.RegisterType<ITest, Test1>(); ITest testInstance1 = unityContainer.Resolve<ITest>(); testInstance1.SayHi(); }
二. 容器的生命周期管理
在真实项目场景,接口往往不可能只会存在一个实现,
Unity是支持一个类型同时注册多个实现的
但是得指定name来进行区分,观察下方代码
成功获取到注册的多个实例
public static void Main(string[] args) { UnityContainer unityContainer = new UnityContainer(); unityContainer.RegisterType<ITest, Test1>("type1"); unityContainer.RegisterType<ITest, Test2>("type2"); ITest testInstance1 = unityContainer.Resolve<ITest>("type1"); ITest testInstance2 = unityContainer.Resolve<ITest>("type2"); testInstance1.SayHi(); testInstance2.SayHi(); }
问题又来了,某些场景,可能存在实例是允许复用的(是线程安全的)
而且实例创建极其消耗资源
这个时候就不能每次获取,都去创建了。
Unity支持生命周期的管理,但是需要注册时去手动指定
最新版本的Unity支持多种生命周期的配置,详情可见 Unity容器中的对象生存期管理 - sangmado - 博客园 (cnblogs.com)
public static void Main(string[] args) { UnityContainer unityContainer = new UnityContainer(); unityContainer.RegisterType<ITest, Test1>("type1"); unityContainer.RegisterType<ITest, Test2>("type2",new SingletonLifetimeManager()); ITest testInstance1 = unityContainer.Resolve<ITest>("type2"); ITest testInstance2 = unityContainer.Resolve<ITest>("type2"); Console.WriteLine($"IsEquals? {ReferenceEquals(testInstance1, testInstance2)}"); }
三. 独立注册到配置文件中
观察上面注册的代码,会发现,IOC并没有起到实际的作用。虽然创建实例没有依赖细节,但是注册的时候都严重依赖了细节
依赖传递,最终我们还是间接的依赖了细节。
所以,比较推介的注册方式是独立到配置文件中。
首先引入nuget包 Unity.Configuration
修改程序配置文件 (控制台为App.config 网站为Web.config) 在configuration节点中
第一行加入以下代码
<configSections> <!--格式为 Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,dll名称 --> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration"/> </configSections>
同时在同级别添加unity 节点
并且在当前项目目录新建unity.config 文件
并设置属性为始终复制
<unity configSource="unity.config" />
unity.config格式如下
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<!--接口和实现存在于哪些dll这里要写完整--> <assembly name="不带后缀名的dll名称"/> <!--引入各个类型所在的命名空间--> <namespace name="命名空间"/> <container>
<!--写注册关系--> <register type="接口类型" mapTo="实现类型" name="">
<constructor><!--指定构造函数参数--> <param name="参数名称" value="值"></param> </constructor>
<!--指定生命周期-->
<lifetime type="singleton"></lifetime> </register> </container> </unity>
如遇到 没有自动补齐,则需要在vs菜单栏 > XML > 架构 中选择 unity.xsd文件
在这里也附上了
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/practices/2010/unity" targetNamespace="http://schemas.microsoft.com/practices/2010/unity" elementFormDefault="qualified" attributeFormDefault="unqualified"> <!-- Core unity config --> <xs:element name="unity" type="UnityConfigurationSection" /> <xs:complexType name="UnityConfigurationSection"> <xs:sequence> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="container" type="ContainerElement"/> <xs:element name="alias" type="AliasElement" /> <xs:element name="sectionExtension" type="SectionExtensionElement" /> <xs:element name="namespace" type="NamedElement" /> <xs:element name="assembly" type="NamedElement" /> </xs:choice> </xs:sequence> </xs:complexType> <xs:complexType name="Containers"> <xs:sequence> <xs:element name="container" minOccurs="0" maxOccurs="unbounded" type="ContainerElement"/> </xs:sequence> </xs:complexType> <xs:complexType name="ContainerElement"> <xs:sequence> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="instance" type="InstanceElement"/> <xs:element name="register" type="RegisterElement"/> <xs:element name="extension" type="ContainerExtensionElement"/> <xs:element name="interception" type="InterceptionElement"/> <xs:element name="interceptors" type="InterceptorsElement"/> </xs:choice> </xs:sequence> <xs:attribute name="name" type="IdentifierName_Type"/> </xs:complexType> <xs:complexType name="InstanceElement"> <xs:attribute name="name" type="IdentifierName_Type"/> <xs:attribute name="type" type="TypeName_Type"/> <xs:attribute name="value" type="LiteralValue_Type"/> <xs:attribute name="typeConverter" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="ContainerExtensionElement"> <xs:attribute name="type" type="ContainerExtensionTypeName_Type" use="required"/> </xs:complexType> <xs:complexType name="RegisterElement"> <xs:group ref="InjectionGroup"/> <xs:attribute name="type" type="TypeName_Type" use="required" /> <xs:attribute name="name" type="IdentifierName_Type" /> <xs:attribute name="mapTo" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="LifetimeElement"> <xs:attribute name="type" type="LifetimeTypeName_Type" use="required"/> <xs:attribute name="value" type="LiteralValue_Type"/> <xs:attribute name="typeConverter" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="ConstructorElement"> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="param" type="ParameterElement"/> </xs:sequence> </xs:complexType> <xs:complexType name="MethodElement"> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="param" type="ParameterElement"/> </xs:sequence> <xs:attribute name="name" type="IdentifierName_Type" use="required"/> </xs:complexType> <xs:complexType name="PropertyElement"> <xs:group ref="ValueResolutionGroup" minOccurs="0"/> <xs:attribute name="name" type="IdentifierName_Type" use="required"/> <xs:attributeGroup ref="ValueOptimizationAttributes"/> </xs:complexType> <xs:complexType name="ParameterElement"> <xs:group ref="ValueResolutionGroup" minOccurs="0"/> <xs:attribute name="name" type="IdentifierName_Type"/> <xs:attribute name="type" type="TypeName_Type"/> <xs:attributeGroup ref="ValueOptimizationAttributes"/> </xs:complexType> <xs:complexType name="DependencyElement"> <xs:attribute name="name" type="IdentifierName_Type"/> <xs:attribute name="type" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="ValueElement"> <xs:attribute name="value" type="LiteralValue_Type"/> <xs:attribute name="typeConverter" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="ArrayElement"> <xs:group ref="ValueResolutionGroup" minOccurs="0" maxOccurs="unbounded"/> <xs:attribute name="type" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="OptionalElement"> <xs:attribute name="name" type="IdentifierName_Type"/> <xs:attribute name="type" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="AliasElement"> <xs:attribute name="alias" type="IdentifierName_Type" use="required"/> <xs:attribute name="type" type="TypeName_Type" use="required"/> </xs:complexType> <xs:complexType name="SectionExtensionElement"> <xs:attribute name="prefix" type="IdentifierName_Type"/> <xs:attribute name="type" type="SectionExtensionTypeName_Type" use="required"/> </xs:complexType> <xs:attributeGroup name="ValueOptimizationAttributes"> <xs:attribute name="value" type="LiteralValue_Type"/> <xs:attribute name="dependencyName" type="IdentifierName_Type"/> <xs:attribute name="dependencyType" type="TypeName_Type"/> </xs:attributeGroup> <xs:group name="ValueResolutionGroup"> <xs:choice> <xs:element name="value" type="ValueElement"/> <xs:element name="dependency" type="DependencyElement"/> <xs:element name="array" type="ArrayElement"/> <xs:element name="optional" type="OptionalElement"/> </xs:choice> </xs:group> <xs:group name="InjectionGroup"> <xs:sequence> <xs:element minOccurs="0" name="lifetime" type="LifetimeElement"/> <xs:element minOccurs="0" name="constructor" type="ConstructorElement"/> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="method" type="MethodElement"/> <xs:element name="property" type="PropertyElement"/> <xs:element name="interceptor" type="InterceptorElement"/> <xs:element name="interceptionBehavior" type="InterceptionBehaviorElement"/> <xs:element name="addInterface" type="AddInterfaceElement"/> <xs:element name="policyInjection" type="PolicyInjectionElement"/> </xs:choice> </xs:sequence> </xs:group> <!-- Interception config --> <xs:complexType name="InterceptionElement"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="policy" type="PolicyElement"/> </xs:choice> </xs:complexType> <xs:complexType name="PolicyElement"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="callHandler" type="CallHandlerElement"/> <xs:element name="matchingRule" type="MatchingRuleElement"/> </xs:choice> <xs:attribute name="name" type="IdentifierName_Type" use="required"/> </xs:complexType> <xs:complexType name="CallHandlerElement"> <xs:group ref="InjectionGroup"/> <xs:attribute name="type" type="TypeName_Type"/> <xs:attribute name="name" type="IdentifierName_Type" use="required"/> </xs:complexType> <xs:complexType name="MatchingRuleElement"> <xs:group ref="InjectionGroup"/> <xs:attribute name="type" type="MatchingRuleTypeName_Type"/> <xs:attribute name="name" type="IdentifierName_Type" use="required"/> </xs:complexType> <xs:complexType name="InterceptorElement"> <xs:attribute name="name" type="IdentifierName_Type"/> <xs:attribute name="type" type="InterceptorTypeName_Type"/> <xs:attribute name="isDefaultForType" type="boolean_Type2"/> </xs:complexType> <xs:complexType name="InterceptionBehaviorElement"> <xs:attribute name="name" type="IdentifierName_Type"/> <xs:attribute name="type" type="TypeName_Type"/> <xs:attribute name="isDefaultForType" type="boolean_Type2"/> </xs:complexType> <xs:complexType name="AddInterfaceElement"> <xs:attribute name="type" type="TypeName_Type" use="required"/> </xs:complexType> <xs:complexType name="PolicyInjectionElement"/> <xs:complexType name="InterceptorsElement"> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="interceptor" type="InterceptorsInterceptorElement"/> </xs:sequence> </xs:complexType> <xs:complexType name="InterceptorsInterceptorElement"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="key" type="KeyElement"/> <xs:element name="default" type="DefaultElement"/> </xs:choice> <xs:attribute name="type" type="InterceptorTypeName_Type"/> <xs:attribute name="value" type="LiteralValue_Type"/> <xs:attribute name="typeConverter" type="TypeName_Type"/> </xs:complexType> <xs:complexType name="KeyElement"> <xs:attribute name="type" type="TypeName_Type" use="required"/> <xs:attribute name="name" type="IdentifierName_Type"/> </xs:complexType> <xs:complexType name="DefaultElement"> <xs:attribute name="type" type="TypeName_Type" use="required"/> </xs:complexType> <!-- common types --> <xs:simpleType name="LiteralValue_Type"> <xs:restriction base="xs:string"/> </xs:simpleType> <xs:simpleType name="TypeName_Type"> <xs:restriction base="xs:string"/> </xs:simpleType> <xs:simpleType name="LifetimeTypeName_Type"> <xs:union memberTypes="TypeName_Type"> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> <xs:enumeration value="singleton"/> <xs:enumeration value="transient"/> <xs:enumeration value="perthread"/> <xs:enumeration value="external"/> <xs:enumeration value="hierarchical"/> <xs:enumeration value="resolve"/> <xs:enumeration value="perresolve"/> </xs:restriction> </xs:simpleType> </xs:union> </xs:simpleType> <xs:simpleType name="ContainerExtensionTypeName_Type"> <xs:union memberTypes="TypeName_Type"> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> <xs:enumeration value="Interception"/> </xs:restriction> </xs:simpleType> </xs:union> </xs:simpleType> <xs:simpleType name="SectionExtensionTypeName_Type"> <xs:union memberTypes="TypeName_Type"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/> </xs:restriction> </xs:simpleType> </xs:union> </xs:simpleType> <xs:simpleType name="InterceptorTypeName_Type"> <xs:union memberTypes="TypeName_Type"> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> <xs:enumeration value="VirtualMethodInterceptor"/> <xs:enumeration value="InterfaceInterceptor"/> <xs:enumeration value="TransparentProxyInterceptor"/> </xs:restriction> </xs:simpleType> </xs:union> </xs:simpleType> <xs:simpleType name="MatchingRuleTypeName_Type"> <xs:union memberTypes="TypeName_Type"> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> <xs:enumeration value="AssemblyMatchingRule"/> <xs:enumeration value="CustomAttributeMatchingRule"/> <xs:enumeration value="MemberNameMatchingRule"/> <xs:enumeration value="NamespaceMatchingRule"/> <xs:enumeration value="ParameterTypeMatchingRule"/> <xs:enumeration value="PropertyMatchingRule"/> <xs:enumeration value="TagAttributeMatchingRule"/> <xs:enumeration value="TypeMatchingRule"/> </xs:restriction> </xs:simpleType> </xs:union> </xs:simpleType> <xs:simpleType name="IdentifierName_Type"> <xs:restriction base="xs:string"/> </xs:simpleType> <xs:simpleType name="boolean_Type2"> <xs:restriction base="xs:boolean"> <xs:pattern value="true" /> <xs:pattern value="false" /> </xs:restriction> </xs:simpleType> <xs:complexType name="Empty"/> <xs:complexType name="NamedElement"> <xs:attribute name="name" type="IdentifierName_Type" use="required"/> </xs:complexType> </xs:schema>
代码中加载
如果节点名称是默认的unity
也可以使用简便的方式
public static IUnityContainer LoadConfig(this IUnityContainer container) { ExeConfigurationFileMap map = new ExeConfigurationFileMap { ExeConfigFilename = "unity.config" }; Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); if (!(config.GetSection("unity") is UnityConfigurationSection section)) { throw new Exception($"配置节点名称 unity.config 未找到"); } _ = container.LoadConfiguration(section); return container; }
//简写
container.LoadConfiguration(section);
可以愉快的Codeing了