Castle Windsor
Part One- Getting Windsor
通过Nuget引入Castle.Windsor
Part Two- Plugging Windsor in
To get started using Windsor we need to plug it into the application.
The first and most publicized MVC extension point that we'll look at is controller factory. We'll use it to have Windsor manage our controllers and all of their dependencies.
通过Mvc 的公开扩展点ControllerFactory,通过它控制Controller和所有的依赖关系。
Windsor Controller Factory
In this part we created a controller factory which uses Windsor to manage components in our application.
Controller factory的两个职责。
(通过Container.IKernel) 为每个request创建新的Controller实例,请求结束后Release Controller实例
为什么不使用IControllerActivator作为扩展点呢?因为它只有创建实例的方法,没有释放实例的方法。 Because it only has a
Create
method, and lacks method for releasing the controllers.
Part Three- Writing First Installer
安装注册项目中的相关Compnent
Windsor 使用Installer类(IWindsorInstaller) registration 程序中的Component(依赖组件、类)
IWindsorInstaller只有一个方法 public void Install(IWindsorContainer container, IConfigurationStore store) ,参数container提供方法注册具体的Component
注意:
1.分类自己的WindsorInstaller,使它们保持简单,可读,职责单一,有利于测试和职责划分查找问题。
2.By default Installers must be public with public default constructor
Using installers
FromAssembly
container.Install(
FromAssembly.This(),//在本程序集中安装
FromAssembly.Named("Acme.Crm.Bootstrap"),//
FromAssembly.Containing<ServicesInstaller>(),
FromAssembly.InDirectory(new AssemblyFilter("Extensions")),
FromAssembly.Instance(this.GetPluginAssembly())
);
安装程序集中所有个IWindsorInstaller
注意:安装顺序不确定,需要确定安装顺序时使用InstallerFactory
FromAssembly.This()
在本程序集中安装
FromAssembly.Named("Acme.Crm.Bootstrap")
Install from assembly with specified assembly name using standard .NET assembly locating mechanism. 通过程序集名字注册
You can also provide path to a .dll or .exe file when you have the assembly in some non-standard location.可以添加程序集的路径
FromAssembly.Containing<ServicesInstaller>()
在存在指定类的程序集中安装,和FromAssembly.Named 二选一。
Installs from assembly containing designated type. This method is usually used as string-less alternative toFromAssembly.Named
.
FromAssembly.InDirectory(new AssemblyFilter("Extensions"))
FromAssembly.Instance
InstallerFactory class 以上方法都有接受InstallerFactory参数的重载,通过它可以控制注册顺序、选择程序集中部分Installer进行注册。
Configuration class 通过配置文件注册Installers
container.Install(
Configuration.FromAppConfig(),
Configuration.FromXmlFile("settings.xml"),
Configuration.FromXml(new AssemblyResource("assembly://Acme.Crm.Data/Configuration/services.xml"))
);
Part Four - Putting it All Together
在Application_Start时
container = new WindsorContainer().Install(FromAssembly.This());//创建容器
var controllerFactory = new WindsorControllerFactory(container.Kernel);//创建ControllerFactory
ControllerBuilder.Current.SetControllerFactory(controllerFactory);//设置当前ControllerFactory
在Application_End时
container.Dispose();//释放容器
Lifestyles
Singleton(default)
第一次请求此Component时创建,以后重复使用。即使调用release也不会起作用,只会在container被释放时销毁。
注意多线程时的状态,保证线程安全。
Transient
瞬时的,当使用它的对象released时,释放Component
注意:如果使用container.Resolve<MyTransientComponent>()需要显示调用container.Release(myTransientInstance) 释放。
Scoped
eq:
using Castle.MicroKernel.Lifestyle;
using (Container.BeginScope()) //extension method
{
var one = Container.Resolve<MyScopedComponent>();
var two = Container.Resolve<MyScopedComponent>();
Assert.AreSame(one, two);
} // releases the instance.
Bound
PerThread
Instance of a component will be shared in scope of a single thread of execution.
It will be created the first time the component is requested on given thread.
Releasing the component explicitly does nothing.
Instances will be released when the container they're registered with is disposed.
单个线程内共享,只有container释放才会回收
注意:Basically it should be used only when your application controls the thread, and never when thread pool threads (or Task
s) are involved.
Pooled
A pool of instances will be created and then one of them will be returned when requested.
When the component is first requested, the pool ofinitialSize
elements is instantiated and a single one of them is marked internally as in use and returned.
When more components are requested, the pool will first return all of the components it has that are not in use, and if it runs out, will start creating new ones.
Releasing the components may do either of two things:
1.When the pool has more components in use thanmaxSize
the component will be released immediately
2.Otherwise the component will be recycled (if it implementsIRecyclable
) and returned to the pool marked as ready to use.
Windsor provides a special interface -Castle.Core.IRecyclable
for poolable components.
It contains single method: void Recycle();
This method is invoked when the component is returned to the pool, and components can use it to implement custom initialization/clean up logic.
Setting lifestyle
API Setting
Container.Register( Classes.FromThisAssembly() .BasedOn<IController>() .LifestyleTransient());
XML Setting
Simple components
<components> <component id="notification" type="Acme.Crm.Services.EmailNotificationService, Acme.Crm"> </component> </components>
This is identical to specifyingComponent.For<EmailNotificationService>().Named("notification");
with Fluent Registration API.
Component with abstraction
<components>
<component
id="notification"
service="Acme.Crm.Services.INotificationService, Acme.Crm"
type="Acme.Crm.Services.EmailNotificationService, Acme.Crm">
</component>
</components>
This is identical to specifying Component.For<INotificationService>().ImplementedBy<EmailNotificationService>().Named("notification");
.
Component with lifestyle
<components>
<component
id="notification"
service="Acme.Crm.Services.INotificationService, Acme.Crm"
type="Acme.Crm.Services.EmailNotificationService, Acme.Crm"
lifestyle="transient">
</component>
</components>
The list of valid values for the lifestyle
attribute (mapped to appropriate LifestyleManager
enum values):
Lifestyle | Notes |
---|---|
singleton | This is the default lifestyle in Windsor |
transient | Windsor keeps references to transient components! |
pooled | For pooled lifestyle two additional attributes need to be defined: initialPoolSize and maxPoolSize both of which accept positive integer values |
thread | |
custom | For custom lifestyle additional attribute needs to be defined: customLifestyleType which points to the type implementing the lifestyle |
Component with parameters
<components>
<component
id="notification"
service="Acme.Crm.Services.INotificationService, Acme.Crm"
type="Acme.Crm.Services.EmailNotificationService, Acme.Crm"
lifestyle="transient">
<parameters>
<smtpServer>localhost:667</smtpServer>
<senderEmail>#{admin.email}</senderEmail>
<emailFormatter>${emailFormatter}</emailFormatter>
</parameters>
</component>
</components>
smtpServer is an inline parameter,也可以传递list、array、dictionary or any other complex types 。(read more)
senderEmail is a property references ,its value is defined in the <properties>
section of the configuration file (not shown here) under name admin.email
. You specify property references using #{property.id}
notation.
emailFormatter is a service override parameter: as its value another component, registered with id equals emailFormatter
will be used. You specify service overrides using ${service.id}
notation.
Component with multiple services (forwarded types)
<components>
<component
id="notification"
service="Acme.Crm.Services.INotificationService, Acme.Crm"
type="Acme.Crm.Services.EmailNotificationService, Acme.Crm">
<forwardedTypes>
<add service="Acme.Crm.Services.IEmailSender, Acme.Crm" />
</forwardedTypes>
</component>
</components>
Setting properties
By default when Windsor creates component it will try to set all its settable properties it can provide value for.
When using XML configuration this behavior can be adjusted.
<component
id="notification"
service="Acme.Crm.Services.INotificationService, Acme.Crm"
type="Acme.Crm.Services.EmailNotificationService, Acme.Crm"
inspectionBehavior="none">
</component>
Inspection behavior | Notes |
---|---|
all | This is the default behavior |
none | Windsor will not try to set any properties |
declaredonly | Only properties declared on the component's type (and not ones declared on base types) will be set |
中文文档
我的笔记