我刚开始使用温莎城堡(3.3.0
),我被困在基于惯例的注册。
我想注册尽可能多的名称约定(IDummyService
-> DummyService
):
var container = new WindsorContainer();
container.Register(Types.FromThisAssembly().Pick().WithServiceDefaultInterfaces());
container.Register(Component.For<IDummyService>().ImplementedBy<DummyService>().LifestyleSingleton()); // So long, I'm throwing here...
…当然,也可以稍微改变一些已注册的组件(改变生命周期管理,构造函数参数等)
因为我想保持注册尽可能简单,所以我想避免复杂的条件。我找到的唯一解决方案很简单-将自定义内容移动到名称约定之上:
var container = new WindsorContainer();
container.Register(Component.For<IDummyService>().ImplementedBy<DummyService>().LifestyleSingleton()); // Do custom stuff first...
container.Register(Types.FromThisAssembly().Pick().WithServiceDefaultInterfaces()); // And convention at the end...
我现在的问题是,这是正确的方式如何解决我的注册?我可以看到温莎城堡在这方面是相当强大的,我宁愿正确地解决我的任务,不幸的是没有看到太多现实世界的例子。
一种选择是通过实现IContributeComponentModelConstruction
接口来进行自定义配置—为每个组件调用ProcessModel
方法:
public class ExtraConfiguration : IContributeComponentModelConstruction
{
public void ProcessModel(IKernel kernel, ComponentModel model)
{
if (model.Implementation == typeof(DummyService))
{
model.LifestyleType = LifestyleType.Singleton;
}
if ...
}
}
在注册其他组件之前,您需要先将其注册到容器中:
container.Kernel.ComponentModelBuilder.AddContributor(new ExtraConfiguration());
你的最后一个注册行是一个非常广泛的,虽然它将工作在一个简单的应用程序,在大多数现实世界的应用程序可能过于简单。
通常,程序集将提供一组或多组服务。使用各种选择方法(如InNamespace、BasedOn、Where),您可以注册每组服务并配置它们的生命周期、依赖关系、命名等。我倾向于为每组服务创建一个单独的方法。(例如RegisterDataAccessComponents()
)非常明确地说明程序集所提供的服务集,可以使以后更容易重新访问代码,弄清楚所提供的是什么,并跟踪影响运行时行为的配置。你仍然按惯例注册,但你做得更明确一些。
为此,我发现创建负责注册和连接程序集提供的服务集的IWindsorInstaller实现也有助于将容器初始化与其他应用程序初始化任务分开。
Castle Windsor确实是一个非常强大和成熟的依赖注入平台。它有很多内部扩展,实际上允许做你的目标。请参阅此处注册工具内建的服务程序集。
我已经使用它将近5年了,对我来说下面的方法是最好的:
// at the startup of the application
_container = (new WindsorContainer()
.AddHelperFacilities() // IWindsorContainer extension that comes from Framework.InversionOfControl
.AddWebApiAdapter() // IWindsorContainer extension that comes from Framework.InversionOfControl.WebApi
.InitializeDomainUsingConventions( // IWindsorContainer extension that comes from Framework.InversionOfControl
AppDomain.CurrentDomain, // domain for which container will be building registrations
"ApplicationName.*", // regext to speed up registration process by processing only services from application namespace
new WebApiControllersRegistrationConvention(), new DefaultRegistrationConvention())); // one or more conventions
// DefaultRegistrationConvention() comes from Framework.InversionOfControl
// WebApiControllersRegistrationConvention() comes from Framework.InversionOfControl.WebApi . A separate assembly to be referenced to avoid extra dependancies on Asp.NET WebApi assemblies
.Resolve<IApplication>.Start(); // resolves application specific entry point and launches the application
然后是框架。InversionOfControl:
namespace Framework.InversionOfControl
{
public static class WindowsContainerExtensions
{
public static IWindsorContainer InitializeDomainUsingConventions(
this IWindsorContainer container, AppDomain appDomain, string commonNamespaceDenominatorMask, params IRegistrationConvention[] registrationConventions)
{
var assembliesToInitialize = new List<Assembly>();
var runtimeAssemblies = new List<Assembly> { Assembly.GetCallingAssembly() };
var processedAssemblies = new List<Assembly>();
runtimeAssemblies.AddRange(appDomain.GetAssemblies());
foreach (var assembly in runtimeAssemblies)
{
ProcessAssembly(assembly, assembliesToInitialize, processedAssemblies, commonNamespaceDenominatorMask, commonNamespaceDenominatorMask == null);
}
var allRuntimeTypes = new List<Type>();
foreach (var assembly in assembliesToInitialize)
{
var assemblyTypes = assembly.GetTypes().ToList();
var installerTypes = assemblyTypes.Where(t => !t.IsInterface && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IWindsorInstaller))).ToArray();
if (installerTypes.Any())
{
foreach (var installer in installerTypes.Select(installerType => (IWindsorInstaller)Activator.CreateInstance(installerType)))
{
container.Install(installer);
}
}
else
{
allRuntimeTypes.AddRange(assemblyTypes);
}
}
foreach (var registrationConvention in registrationConventions)
{
registrationConvention.RegisterTypesUsingConvention(container, allRuntimeTypes);
}
return container;
}
private static void ProcessAssembly(Assembly assembly, List<Assembly> assemblies, List<Assembly> processedAssemblies, string commonNamespaceDenominatorMask, bool fullScan)
{
if (processedAssemblies.Any(x => x.FullName == assembly.FullName)) return;
if (assembly == typeof(WindowsContainerExtensions).Assembly) return;
processedAssemblies.Add(assembly);
var initialize = (new Regex(commonNamespaceDenominatorMask, RegexOptions.IgnoreCase)).Match(assembly.FullName).Success;
if (initialize && assemblies.Any(x => x.FullName == assembly.FullName))
{
initialize = false;
}
if (initialize)
{
assemblies.Add(assembly);
}
foreach (var referencedAssembliyNames in assembly.GetReferencedAssemblies())
{
var referencedAssembliyNames1 = referencedAssembliyNames;
if (assemblies.Any(x => x.FullName == referencedAssembliyNames1.FullName)) continue;
if (fullScan == false && (new Regex(commonNamespaceDenominatorMask, RegexOptions.IgnoreCase)).Match(assembly.FullName).Success == false) continue;
Assembly referencedAssembliy;
try
{
referencedAssembliy = Assembly.Load(referencedAssembliyNames);
}
catch
{
continue;
}
ProcessAssembly(referencedAssembliy, assemblies, processedAssemblies, commonNamespaceDenominatorMask, fullScan);
}
}
public static IWindsorContainer AddHelperFacilities(this IWindsorContainer container)
{
container.AddFacility<TypedFactoryFacility>();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Register(Component.For<IWindsorContainer>().ImplementedBy<WindsorContainer>());
container.Register(Component.For<IContainerAccessor>().ImplementedBy<ContainerAccessor>());
container.Resolve<IContainerAccessor>().Container = container;
return container;
}
}
public interface IRegistrationConvention
{
IWindsorContainer RegisterTypesUsingConvention(IWindsorContainer container, List<Type> assemblyTypes);
}
public class DefaultRegistrationConvention : IRegistrationConvention
{
/// <summary>
/// Register every service possible from the calling assembly with default singleton lifestyle
/// with the exception of ISomething Factory where the the ISomething GetSomething() where
/// Something that implements ISomething is registered with transient lifestyle
/// </summary>
public IWindsorContainer RegisterTypesUsingConvention(IWindsorContainer container, List<Type> assemblyTypes)
{
// Step 1: Factories installation.
// We register interfaces ending 'Factory' keyword like proxy (implementionless) factories.
var factoryServices = new List<Type>();
var factorySelector = new FullNameFactorySelector();
foreach (var factoryType in assemblyTypes.Where(t => t.Name.EndsWith("Factory") && t.IsInterface))
{
foreach (var method in factoryType.GetMethods())
{
if (method.Name.StartsWith("Get") == false) continue;
if (method.ReturnType.IsInterface == false) continue;
factoryServices.Add(method.ReturnType);
}
container.Register(Component.For(factoryType).AsFactory(factorySelector));
}
// Step 2: Rest of the services registrations
// transientServices list is populated with services that needs to has transient lifespan
// everything else needs to go as preconfigured lifestyle - lifeStyleType
var components = assemblyTypes.Where(t => !t.IsInterface && !t.IsAbstract);
foreach (var component in components)
{
// for every interface and implementation do registration
foreach (var service in component.GetInterfaces())
{
IRegistration registration;
Type service1 = service;
if (factoryServices.Any(x => x.FullName == service1.FullName))
{
if (component.IsGenericType)
{
// GetInterfaces() and GetMethod().ReturnType both returns Type.FullName = null
// Castle.Windsor fails to Resolve later generic types if registered type is with FullName = null,
// Workaround is to find the type with correct FullName from the 'assemblyTypes'
var serviceWithFullName = assemblyTypes.FirstOrDefault(x => x.Name == service1.Name);
if (serviceWithFullName == null) continue; // if null then the mapping is not supported by this convention
registration = Component.For(serviceWithFullName)
.ImplementedBy(component)
.LifestyleTransient()
.Named(serviceWithFullName.FullName + " / " + component.FullName);
}
else
{
registration = Component.For(service)
.ImplementedBy(component)
.LifestyleTransient()
.Named(service.FullName + " / " + component.FullName);
}
}
else
{
registration = Component.For(service)
.ImplementedBy(component)
.Named(service.FullName + " / " + component.FullName)
.LifeStyle.Is(LifestyleType.Singleton);
}
container.Register(registration);
}
}
return container;
}
}
}
以上所有做城堡温莎已经通过模块化和可扩展的方式,而不限制城堡温莎的能力。只用几行,它就注册了整个应用程序的以下约定,并允许添加特定的约定,如:Mvc, WebApi, AutoMapper, Wcf, Quartz和其他
有几个ConfigureX
方法用于此目的,即ConfigureIf
或基于类型的ConfigureFor<IDummyService>
。
这是相关文档的链接