Introducing Unity Application Block
Unity Block学习笔记
1 What's Unity?
1.1 轻量DI容器
1.1.1 traditional
1.1.1.1 constructor
1.1.1.2 setter
1.1.1.3 interface
1.1.2 Unity 扩展
1.1.2.1 property
1.1.2.2 method call
1.2 与其他EntLib的差异
1.2.1 可独立安装的DI机制
1.2.2 解决DI的途径
1.2.2.1 通过配置
1.2.2.2 通过代码动态注册
1.2.3 不依赖于EntLib的Core和Configuration部分
1.3 优势
1.3.1 简化对象的创建过程,尤其是层次型对象
1.3.2 借助横切方式,提供必要的抽象,对象创建的定义可以通过配置或运行时方式完成
1.3.3 将构造过程延迟到依据容器的配置
1.3.4 提供Service Locator功能
1.3.4.1 container保存位置
1.3.4.1.1 ASP.NET Session
1.3.4.1.2 ASP.NET Application
1.3.4.1.3 其他Client Store
2 Unity的常用情景
2.1 背景
2.1.1 由于业务的复杂性,现代软件进入基于组件的软件工程时代
2.1.2 包括业务处理组件、公共机制、还有一系列横切机制
2.1.3 必须采用松散耦合设计
2.1.4 但对象的创建过程往往又存在严格的依赖关系
2.2 常用的解决模式
2.2.1 IoC模式
Inversion of Control (IoC) pattern. This generic pattern describes techniques for supporting a plug-in architecture where objects can "look up" instances of other objects they require.
2.2.2 DI模式
Dependency Injection (DI) pattern. This is a special case of the IoC pattern and is an interface programming technique based on altering class behavior without the changing the class internals. Developers code against an interface for the class and use a container that injects dependent object instances into the class based on the interface or object type. The techniques for injecting object instances are interface injection, constructor injection, property (setter) injection, and method call injection.
2.2.3 Interception模式
Interception pattern. This pattern introduces another level of indirection. This technique places an object between the client and the real object. A proxy is used between the client and the real object. The client behavior is the same as interacting directly to the real object, but the proxy intercepts them and solves their execution by collaborating with the real object and other objects as required.
3 开发应用
3.1 环境要求
This topic describes the requirements for installing and using the Unity Application Block. It also discusses how you can extend and modify the application block using different versions of Visual Studio. The minimum requirements are the following:
Microsoft Windows XP Professional, Windows Server 2003, Windows Server 2008, or Windows Vista operating system
Microsoft .NET Framework 2.0, 3.0, or 3.5
Microsoft Visual Studio 2005 or Visual Studio 2008 development system (any of the following editions):
The Express Editions:
Visual Basic 2008 Express Edition
Visual C# 2008 Express Edition
Visual C++ 2008 Express Edition
Visual Web Developer 2008 Express Edition
Standard Edition
Professional Edition
Team Edition for Software Developers
Team Edition for Software Testers
Team Edition for Software Architects
Team Suite
The Unity Application Block solution and project files are all in Visual Studio 2005 format, and you can open them and modify them using Visual Studio 2005. The binary assemblies provided with the application block are targeted at version 2.0 of the .NET Framework. However, you can use the Unity Application Block in applications created with Visual Studio 2008 by setting a reference to the binary assemblies.
You can modify or extend the Unity Application Block using Visual Studio 2008. When you open a solution, Visual Studio 2008 will upgrade the projects to its format, and you can edit and compile the code to create assemblies targeted at version 3.5 of the .NET Framework. However, you will not be able to convert the projects back into Visual Studio 2005 format. Therefore, it is a good idea to work with a copy of the original solutions and projects.
3.2 构造类型实例
3.2.1 构造一个最简单的对象实例
publicinterfaceIUser{ }
publicclassUserA: IUser{ }
publicclassUserB: IUser{ }
publicclassObjectWithTwoProperties
{
objectobj1;
objectobj2;
[Dependency]
publicobjectObj1
{
get{ returnobj1; }
set{ obj1 = value; }
}
[Dependency]
publicobjectObj2
{
get{ returnobj2; }
set{ obj2 = value; }
}
publicvoidInjectInt(intmax)
{
this.max = max;
}
[InjectionMethod]
publicvoidInjectString(stringmessage, intcount)
{
this.message = message;
this.count = count;
}
}
[TestMethod]
publicvoidTestCreateInstanceByInterface()
{
IUnityContainercontainer = newUnityContainer();
container.RegisterType<IUser, UserA>();
IUseruserA = container.Resolve<IUser>();
Assert.IsInstanceOfType(userA,typeof(UserA));
}
[TestMethod]
publicvoidTestSetterInjection()
{
IUnityContainercontaienr = newUnityContainer();
ObjectWithTwoPropertiestarget = newObjectWithTwoProperties();
Assert.IsNull(target.Obj1);
Assert.IsNull(target.Obj2);
contaienr.BuildUp(target);
Assert.IsNotNull(target.Obj1);
Assert.IsNotNull(target.Obj2);
}
3.2.2 为映射关系命名
publicinterfaceIUser
{
}
publicclassDefaultUser: IUser
{
}
publicclassUserA: IUser
{
}
publicclassUserB: IUser
{
}
[TestMethod]
publicvoidTestCreateDefaultTypeInstance()
{
IUnityContainercontainer = newUnityContainer()
.RegisterType<IUser, DefaultUser>()
.RegisterType<IUser, UserA>("A")
.RegisterType<IUser, UserB>("B");
IUseruser = container.Resolve<IUser>();
Assert.IsInstanceOfType(user,typeof(DefaultUser));
}
[TestMethod]
publicvoidTestCreateNamedTypeInstance()
{
IUnityContainercontainer = newUnityContainer()
.RegisterType<IUser, DefaultUser>()
.RegisterType<IUser, UserA>("A")
.RegisterType<IUser, UserB>("B");
IUseruserA = container.Resolve<IUser>("A");
Assert.IsInstanceOfType(userA,typeof(UserA));
IUseruserB = container.Resolve<IUser>("B");
Assert.IsInstanceOfType(userB,typeof(UserB));
}
3.2.3 构造一系列简单的对象实例
publicinterfaceIUser
{
}
publicclassDefaultUser: IUser
{
}
publicclassUserA: IUser
{
}
publicclassUserB: IUser
{
}
[TestMethod]
publicvoidTestCreateSeriesInstances()
{
IUnityContainercontainer = newUnityContainer()
.RegisterType<IUser, UserA>("A")
.RegisterType<IUser, UserB>("B");
IList<IUser> users = newList<IUser>(container.ResolveAll<IUser>());
Assert.IsInstanceOfType(users[0],typeof(UserA));
Assert.IsInstanceOfType(users[1],typeof(UserB));
}
3.2.4 Singleton实例
publicinterfaceIUser
{
}
publicclassDefaultUser: IUser
{
}
publicclassUserA: IUser
{
}
publicclassUserB: IUser
{
}
[TestMethod]
publicvoidTestCreateSingletonInstance()
{
IUnityContainercontainer = newUnityContainer()
.RegisterType<IUser, DefaultUser>(newContainerControlledLifetimeManager());
IUseruserA = container.Resolve<IUser>();
IUseruserB = container.Resolve<IUser>();
IUseruserC = container.Resolve<IUser>();
Assert.IsNotNull(userA);
Assert.IsNotNull(userB);
Assert.IsNotNull(userC);
Assert.AreSame(userA, userB);
Assert.AreSame(userB, userC);
container.Dispose();
}
3.2.5 MethodInjection
publicclassObjectWithTwoProperties
{
intmax;
stringmessage;
intcount;
publicintMax
{
get{ returnmax; }
}
publicstringMessage
{
get{ returnmessage; }
}
publicintCount
{
get{ returncount; }
}
[InjectionMethod]
publicvoidInjectInt(intmax)
{
this.max = max;
}
[InjectionMethod]
publicvoidInjectString(stringmessage, intcount)
{
this.message = message;
this.count = count;
}
}
[TestMethod]
publicvoidTestMethodInjection()
{
IUnityContainercontainer = newUnityContainer();
container.RegisterType<ObjectWithTwoProperties>(
newInjectionMethod("InjectInt", 30),
newInjectionMethod("InjectString", "Hello", 15));
ObjectWithTwoPropertiestarget = container.Resolve<ObjectWithTwoProperties>();
Assert.IsNotNull(target);
Assert.AreEqual<int>(30, target.Max);
Assert.AreEqual<string>("Hello", target.Message);
Assert.AreEqual<int>(15, target.Count);
}
3.2.6 ConstructorInjection
publicclassDepObj1
{
publicstringValue;
}
publicclassDepObj2
{
publicstringValue;
}
publicclassUserObj
{
stringv1;
stringv2;
[InjectionConstructor]
publicUserObj(DepObj1obj1, DepObj2obj2)
{
v1 = obj1.Value;
v2 = obj2.Value;
}
publicstringV1
{
get{ returnv1; }
}
publicstringV2
{
get{ returnv2; }
}
}
conststringV1 = "Hello";
conststringV2 = "World";
[TestMethod]
publicvoidTestConstructorInjection()
{
IUnityContainercontainer = newUnityContainer();
UserObjtarget1 = container.Resolve<UserObj>();
Assert.IsNotNull(target1);
Assert.IsTrue(string.IsNullOrEmpty(target1.V1));
Assert.IsTrue(string.IsNullOrEmpty(target1.V2));
DepObj1obj1 = newDepObj1();
obj1.Value = V1;
DepObj2obj2 = newDepObj2();
obj2.Value = V2;
container.RegisterInstance<DepObj1>(obj1);
container.RegisterInstance<DepObj2>(obj2);
UserObjtarget2 = container.Resolve<UserObj>();
Assert.IsNotNull(target2);
Assert.AreEqual<string>(V1, target2.V1);
Assert.AreEqual<string>(V2, target2.V2);
}
3.2.7 根据现有实例构造
publicinterfaceIUser
{
}
publicclassUserA: IUser{ }
publicclassUserB: IUser{ }
[TestMethod]
publicvoidTestCreateByInstance()
{
UserAuserA = newUserA();
UserBuserB = newUserB();
IUnityContainercontainer = newUnityContainer()
.RegisterInstance<IUser>(userA)
.RegisterInstance<IUser>("special", userB);
IUseru1 = container.Resolve<IUser>();
IUseru2 = container.Resolve<IUser>("special");
Assert.IsInstanceOfType(u1,typeof(UserA));
Assert.IsInstanceOfType(u2,typeof(UserB));
}
3.3 用Unity构造并应用
publicinterfaceIUser
{
stringName { get;}
intAge { get;}
}
publicclassUserA: IUser
{
#regionIUser Members
publicstringName { get{ return"Joe"; } }
publicintAge { get{ return20; } }
#endregion
}
classUserStore
{
IUseruser;
publicUserStore(IUseruser)
{
this.user = user;
}
publicstringUserInformatiion
{
get
{
if(this.user == null)
thrownewArgumentNullException("user");
returnstring.Format("{0} is {1} years old", user.Name, user.Age);
}
}
}
///<summary>
///Constructor Injection
///</summary>
[TestMethod]
publicvoidTestConstructorInjection()
{
IUnityContainercontainer = newUnityContainer();
container.RegisterType<IUser, UserA>();
IUseruser = container.Resolve<IUser>();
UserStorestore = newUserStore(user);
Assert.AreEqual<string>(
string.Format("{0} is {1} years old","Joe", 20),
store.UserInformatiion);
}
4 Unity的设计
4.1 概要类图
4.2 初始化过程
When code initializes a UnityContainer instance, the constructor creates within its context instances of the classes required to prepare ObjectBuilder and pass information to it so that it can perform the appropriate object generation process. These classes are defined by ObjectBuilder and include the following:
· The NamedTypesRegistry class, which stores a list of registered types
· A generic List that holds references to UnityContainerExtension instances added to the container
· The Locator class, which locates existing instances of registered types
· The LifetimeContainer class, which manages the lifetime of registered objects and disposes of them when the container is disposed
· Two instances of the StagedStrategyChain, which store the build strategies and build plans for ObjectBuilder to use when creating instances of registered types
· The PolicyList class, which contains the policies ObjectBuilder will use when examining objects and classes during the build process
During the next stage of initialization, the UnityContainer adds two container extensions named UnityDefaultStrategiesExtension and UnityDefaultBehaviorExtension that implement and manage the Unity processes.
The UnityDefaultStrategiesExtension adds to the container context the two StagedStrategyChain instances created during initialization that specify the main strategy chain and the build plan strategy chain. These strategy chains determine how ObjectBuilder will handle constructor injection, property injection, and method call injection. The UnityDefaultStrategiesExtension also adds three ObjectBuilder policies to the current container context that specify the three attributes (the InjectionConstructor, Dependency, and InjectionMethod attributes) that ObjectBuilder will look for when reflecting over the object it creates.
The UnityDefaultBehaviorExtension registers event handlers for the type registration events that Unity supports:
· Registering. This registers the type name in the container context and adds a BuildKeyMappingPolicy to the context that specifies how ObjectBuilder will resolve type mappings. It also adds any specified lifetime manager to the context lifetime policies.
· RegisteringInstance. This registers the instance in the container context and adds any specified lifetime manager to the context lifetime policies.
4.3 提供的操作
The public methods of the UnityContainer that developers use fall into two main categories:
· Methods that register mappings or types. These methods raise the Registering or the RegisteringInstance event. This causes the UnityDefaultBehaviorExtension to create the appropriate policies and locator entries within the current container context.
· Methods that retrieve objects. These include overrides of the Resolve, ResolveAll, and BuildUp methods. All these methods eventually call a private method of the UnityContainer class named DoBuildUp. This method retrieves the required instances of the objects that ObjectBuilder requires to perform the object creation process. These objects include the NamedTypesRegistry, Locator, LifetimeContainer, the list of container extensions, and the strategies and policies created during initialization and through registration of type mappings, singletons, and object instances. The DoBuildUp method then executes the BuildUp method in ObjectBuilder and returns the resulting object.
posted on 2009-08-30 23:10 Utopia Coming 阅读(640) 评论(0) 编辑 收藏 举报