微软企业库Unity学习笔记
本文主要介绍:
关于Unity container配置,注册映射关系、类型,单实例、已存在对象和指出一些container的基本配置,这只是我关于Unity的学习心得和笔记,希望能够大家多交流相互学习到更多知识,谢谢大家的支持。
我们可以通过以下两种方法给Unity container中创建映射:
- RegisterType:这个方法可以往container中注册一种类型或映射关系,当我们需要调用该类型的实例时,container会自动实例化该类型的对象,无需通过new someName方法实例化对象(例如:使用Resolve或ResolveAll方法获取注册类型的实例),当没有指定实例化对象的生命周期,将使用默认的TransientLifetimeManager(每次调用Resolve或ResolveAll方法时都会实例化一个新的对象)。
- RegisterInstance:这个方法是往container中注册一个已存在实例,通过Resolve或ResolveAll方法获取该类型的实例,默认使用ContainerControlledLifetimeManager管理对象生命周期,而且container中会保持对象的引用(简而言之每次调用Resolve或ResolveAll方法都会调用同一个对象的引用)。
1.1 注册一个接口或类的映射到具体类型:
使用Register方法可以往容器中注册一种类型或映射关系
- l 往容器中注册一种类型RegisterType<Type>
- l 往容器中注册一种映射关系RegisterType< RegisteredType, TargetType >
调用RegisterType<RegisteredType, TargetType>()方法可以往容器中注册一种映射关系,RegisteredType是一个接口或类,而TargetType必需实现该接口或继承该类。
1.2 注册一个单例类或单例类型实例:
涉及到对象的生命周期(Lifetime Managers):
- l 默认的情况下使用TransientLifetimeManager管理对象的生命周期,以下两种方法效果一样:
container.RegisterType<IMyService, CustomerService>(new TransientLifetimeManager());
- l 使用ContainerControlledLifetimeManager创建单例实例
- l 使用ExternallyControlledLifetimeManager只可以保持对象的弱引用
关于强引用和弱引用:
强引用:当一个根指向一个对象时,该对象不可能被执行垃圾收集,因为程序代码还在访问该对象。
弱引用:允许垃圾收集器收集对象,同时允许应用程序访问该对象,结果是执行哪个要取决于时间。
- l 使用PerThreadLifetimeManager在同一个线程返回同一个对象实例,不同线程对象实例不相同
在使用RegisterType注册类型或映射,如果对对象生命周期进行,将使用默认的TransientLifetimeManager,它不会在container中保存对象的引用,简而言之每当调用Resolve或ResolveAll方法时都会实例化一个新的对象。当我们想在container中保存对象的引用的时我们可以使用ContainerControlledLifetimeManager管理对象的生命周期,简而言之每次调用Resolve或ResolveAll方法都会调用同一个对象的引用。
1.3 注册一个存在对象作为单件实例:
注册一个存在的对象我们使用的是RegisterInstance,它默认使用ContainerControlledLifetimeManager管理对象的生命周期,回顾以下
RegisterType和RegisterInstance区别:
Method |
Default Lifetime Managers |
Same Lifetime Managers |
RegisterType |
TransientLifetimeManager |
ContainerControlledLifetimeManager |
RegisterInstance |
ContainerControlledLifetimeManager |
ContainerControlledLifetimeManager |
当RegisterType和RegisterInstance都使用ContainerControlledLifetimeManager时的区别:
MSDN:If you used the RegisterType method to register a type, Unity creates a new instance of the registered type during the first call to the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. Subsequent requests return the same instance.
If you used the RegisterInstance method to register an existing object, Unity returns this instance every time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes.
翻译:如果使用RegisterType方法注册一种类型,当第一次调用Resolve 或ResolveAll 方法或依赖机制注入一个实例到其他类时,Unity会实例化该注册类型,之后的调用都返回相同实例。
如果使用RegisterInstance方法注册一个存在对象,当我们调用Resolve和ResolveAll方法或赖机制注入一个实例到其他类时,Unity每次都返回该对象实例。
1.4 使用container的fluent interface属性:
这意味着你可以在一条语句中使用方法链
.RegisterType<IMyService, DataService>()
.RegisterType<IMyUtilities, DataConversions>()
.RegisterInstance<IMyService>(myEmailService);
由于IUnityContainer 接口实现了IDispose接口,所有我们可以使用Using控制container资源释放
////using有效释放对象占用的系统资源。
using (IUnityContainer container =new UnityContainer())
{
////实现
}
接下来介绍一下依赖注入的方式:
- 构造函数注入
- 属性注入
- 方法注入
一、 构造函数注入
我们将介绍单构造函数和多构造函数注入
1) 单构造函数使用自动注入
- 单构造函数自动注入,这里我们使用一个简单的例子具体类MyObject依赖于具体类MyDependentClass。
publicclass MyOjbect
{
privatestring description;
publicstring Description
{
get { return description; }
set { description = value; }
}
public MyOjbect(MyDependentClass myDependent)
{
this.Description = myDependent.Description;
}
}
MyOjbect objlist = container.Resolve<MyOjbect>();
- 当然除了具体类型我们还可以使用接口或者基类类型作为参数注入依赖,这里我们定义一个接口和一个抽象类。
//// IMyService是一个接口,objMyService实现该接口
publicclass MyOjbect
{
privatestring description;
publicstring Description
{
get { return description; }
set { description = value; }
}
public MyOjbect(MyServiceBase objIMyService, IMyService objMyService)
{
////TODO:
}
}
container.RegisterType<IMyService, MyServiceDependent>();
container.RegisterType<MyServiceBase, DataService>();
MyOjbect objlist = container.Resolve<MyOjbect>();
2) 多重载构造函数通过属性指定注入构造函数
通过InjectionConstructor属性指定要注入构造函数,和单构造函数功能一样,但要注意一点是当有多重载构造函数,如果我们没有使用属性指定注入构造函数,Unity会根据构造函数参数最多的进行依赖注入,如果不存在唯一最多参数的构造函数(例如:有两个或者两个以上构造函数具有最多参数时候),Unity会抛出异常。
{
privatestring description;
publicstring Description
{
get { return description; }
set { description = value; }
}
public MyOjbect(MyServiceBase objIMyService)
{
////
}
[InjectionConstructor]
public MyOjbect(MyServiceBase objIMyService, IMyService objMyService)
{
////
}
}
通过前面的介绍我们初步明白了Unity的作用就是给我们一个更方便实现类与类的解耦,假设在一般情况一个类依赖于其他类的时候,我们必须实例化该依赖类,然后把实例传递给我们要调用类,但有了Unity它帮我们实例了这些。OK接下来讲一个具体例子。
/// 父母类
///</summary>
publicclass Parent
{
privatereadonly ChildA _classA;
privatereadonly ChildB _classB;
public Parent() { }
//指定依赖注入构造函数
[InjectionConstructor]
public Parent(ChildA chA, ChildB chB)
{
this._classA = chA;
this._classB = chB;
}
publicoverridestring ToString()
{
// 年长的父母依赖与孩子。
returnstring.Format("The elder depend on {0} and {1}.", this._classA.ToString(), this._classB.ToString());
}
}
///<summary>
/// 孩子类
///</summary>
publicclass ChildA : Parent
{
publicoverridestring ToString()
{
return"ChildA";
}
}
///<summary>
/// 孩子类
///</summary>
publicclass ChildB : Parent
{
publicoverridestring ToString()
{
return"ChildB";
}
}
class Program
{
staticvoid Main(string[] args)
{
using (IUnityContainer container =new UnityContainer())
{
Parent objParent = container.Resolve<Parent>();
Console.WriteLine(objParent.ToString());
Console.ReadKey();
}
}
}
接下通过一个简单的配置文件实例例子进一步说明,Unity是如何实现类与类之间的解耦的。
首先我们先定义一个Foo类,它依赖于一个接口ILogger,它有两个实现分别是LoggerA和LoggerB,然后我们的Foo类要调用LoggerA。在一般情况下我们可以实例化一个LoggerA的实例,然后传递给Foo就OK了,这就是hard code给我们带来了一个紧的耦合,如果要我们修改成调用LoggerB,那么我们可以再实例化一个LoggerB对象,眼前看来是可以这样实现,但如果我们工程很大,这种做法是十分危险,当我们使用配置文件可以简单实现,来讲讲简单实现。
{
using (IUnityContainer container =new UnityContainer())
{
////Parent objParent = container.Resolve<Parent>();
////Console.WriteLine(objParent.ToString());
////Console.ReadKey();
//获取指定名称的配置节
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
//获取container名称为CfgClass下的配置
section.Containers["CfgClass"].Configure(container);
Foo myFoo = container.Resolve<Foo>();
Console.WriteLine(myFoo.ToString());
Console.ReadKey();
}
}
App.config中的配置如下:
<configSections>
<!-- 每次都必须定义的开头 -->
<section name ="unity" type ="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration"/>
</configSections>
<!-- 使用unity的xsd -->
<!--<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">-->
<unity>
<!-- 声明一种是用类型 -->
<typeAliases>
<typeAlias alias ="ILogger" type ="MSEnterpriseUnitity.ILogger, MSEnterpriseUnitity"/>
</typeAliases>
<containers>
<container name="CfgClass">
<!-- 设定该类型的映射关系 -->
<types>
<type type ="ILogger" mapTo="MSEnterpriseUnitity.LoggerA, MSEnterpriseUnitity"/>
</types>
</container>
</containers>
</unity>
</configuration>
二、方法注入
当一个类的方法依赖于其他类的时候(例如构造函数参数依赖与其他类),在一般情况下通过实例化该被依赖类对象,然后将参数传递给我们依赖类的方法,如果使用方法注入可以避免实例化被依赖类对象,这里就仅仅需要在依赖方法中设置一个属性InjectionMethod就OK了。
2.1方法依赖于具体类
首先我们定义两个类MyTargetOjbect和OtherObject,然后定义在MyTargetOjbect中定义一个方法Initialize(OtherObject dep),该方法的参数依赖于OtherObject类。
/// 依赖类包含一个Description和OutPut方法。
///</summary>
publicclass OtherObject
{
privatestring _description;
publicstring Description
{
get { return _description; }
set { _description = value; }
}
publicvoid OutPut()
{
Console.WriteLine(this.Description);
}
}
/// 目标类的方法Initialize参数依赖于OtherObject类。
///</summary>
publicclass MyTargetOjbect
{
public OtherObject dependentObject;
[InjectionMethod]
publicvoid Initialize(OtherObject dep)
{
this.dependentObject = dep;
}
}
{
MyTargetOjbect myTargetObject = container.Resolve<MyTargetOjbect>();
myTargetObject.dependentObject.Description ="Injection successful.";
myTargetObject.dependentObject.OutPut();
}
2.2 方法依赖于接口或者基类
定义一个抽象类MyBaseClass,然后MyInheritBase继承该抽象类,定义一个接口IMyInterface然后ImplementInterface实现该接口。
/// 接口包含一个属性和一个方法
///</summary>
publicinterface IMyInterface
{
string Description
{
get;
set;
}
void OutPut();
}
/// 实现IMyInterface的属性和方法
///</summary>
publicclass ImplementInterface : IMyInterface
{
privatestring _description;
#region IMyInterface 成员
publicstring Description
{
get
{
return _description;
}
set
{
_description = value;
}
}
publicvoid OutPut()
{
Console.WriteLine(this.Description);
}
#endregion
}
/// 一个抽象类包含一个属性和一个方法
///</summary>
publicabstractclass MyBaseClass
{
privatestring _description;
publicvirtualstring Description
{
get { return _description; }
set { _description = value; }
}
publicabstractvoid OutPut();
}
/// 实现抽象类的抽象方法
///</summary>
publicclass MyInheritBase : MyBaseClass
{
publicoverridevoid OutPut()
{
Console.WriteLine(this.Description);
}
}
/// 目标类的方法Initialize参数依赖于OtherObject类。
///</summary>
publicclass MyTargetOjbect
{
public IMyInterface myInterface;
public MyBaseClass myBase;
[InjectionMethod]
publicvoid Initialize(IMyInterface myInterface, MyBaseClass myBase)
{
this.myInterface = myInterface;
this.myBase = myBase;
}
}
container.RegisterType<MyBaseClass, MyInheritBase>();
container.RegisterType<IMyInterface, ImplementInterface>();
MyTargetOjbect myTargetObject = container.Resolve<MyTargetOjbect>();
myTargetObject.myInterface.Description ="Injection Successful.";
myTargetObject.myInterface.OutPut();
myTargetObject.myBase.Description ="Injection Successful.";
myTargetObject.myBase.OutPut();
三、属性注入
当一个类的属性依赖于其他类,一般情况初始化该属性需要实例化该类型对象,然而我们可以使用Dependency属性,指定属性注入无限手动实例化类型对象。
3.1 具体类型属性
{
private SomeOtherObject _dependentObject;
[Dependency]
public SomeOtherObject DependentObject
{
get { return _dependentObject; }
set { _dependentObject = value; }
}
}
IUnityContainer uContainer =new UnityContainer();
MyObject myInstance = uContainer.Resolve<MyObject>();
// now access the property containing the dependency
SomeOtherObject depObj = myInstance.DependentObject;
3.2抽象类或接口属性
{
private IMyInterface _interfaceObj;
private MyBaseClass _baseObj;
[Dependency]
public IMyInterface InterfaceObject
{
get { return _interfaceObj; }
set { _interfaceObj = value; }
}
[Dependency]
public MyBaseClass BaseObject
{
get { return _baseObj; }
set { _baseObj = value; }
}
}
IUnityContainer uContainer =new UnityContainer()
.RegisterType<IMyInterface, FirstObject>()
.RegisterType<MyBaseClass, SecondObject>();
MyObject myInstance = uContainer.Resolve<MyObject>();
// now access the properties containing the dependencies
IMyInterface depObjA = myInstance.InterfaceObject;
MyBaseClass depObjB = myInstance.BaseObject;
3.3 给属性注入命名
给属性命名只需在Dependency(“name”)定义一个名字就OK了。
{
public IMyInterface myInterface;
public MyBaseClass myBase;
private IMyInterface _MyProperty1, _MyProperty2;
//命名属性注入
[Dependency("Property2")]
public IMyInterface MyProperty2
{
get { return _MyProperty2; }
set { _MyProperty2 = value; }
}
//命名属性注入
[Dependency("Property1")]
public IMyInterface MyProperty1
{
get { return _MyProperty1; }
set { _MyProperty1 = value; }
}
}
container.RegisterType<IMyInterface, ImplementInterface>("Property1");
//调用Property2属性注入
container.RegisterType<IMyInterface, ImplementInterface2>("Property2");
MyTargetOjbect myTargetObject = container.Resolve<MyTargetOjbect>();
IMyInterface myProperty1 = myTargetObject.MyProperty1;
IMyInterface myProperty2 = myTargetObject.MyProperty2;
3.4 构造函数参数的属性注入
构造函数参数依赖于其他类时候,我们可以考虑使用构造函数注入或者属性注入
{
private ILogger _iLogger;
public Foo([Dependency] ILogger iLogger)
{
this._iLogger = iLogger;
}
publicoverridestring ToString()
{
returnstring.Format("Foo depends on {0}.", this._iLogger.ToString());
}
}