我实际上找到了一个很好的解决方案,我将 xml 文件结合起来进行覆盖,并使用流利的注册作为默认值。
fluent-API 将 impl 的全名作为默认键。在运行中,我覆盖了 xml-config 的 id 以模仿 fluent-API 的键约定。
然后我一边听一边注册 xml-config Kernel.ComponentRegistered
。
之后,我只从 xml 尚未定义服务的代码配置中添加服务。
(这是不久前,我只是复制粘贴代码。希望你能让它工作。如果你发现任何问题,我会进行编辑)
IList<Type> unnamedServices = new List<Type>();
IDictionary<string, Type> namedServices = new Dictionary<string, Type>();
ComponentDataDelegate registered = captureRegistrations(unnamedServices, namedServices);
container.Kernel.ComponentRegistered += registered;
// The method that captures the services
private static ComponentDataDelegate captureRegistrations(
IList<Type> unnamedServices, IDictionary<string, Type> namedServices)
{
return (key, handler) =>
{
if (handler.ComponentModel.Name == handler.ComponentModel.Implementation.FullName)
{
unnamedServices.Add(handler.Service);
}
else
{
namedServices.Add(key, handler.Service);
}
};
}
之后,在我用代码注册服务之前,我会检查它们是否已经注册。我还创建了一个基类,使这更容易。这是一个应用程序配置:
public class ApplicationConfiguration : WindsorConfigurationSkeleton
{
internal static WindsorServiceLocator create()
{
var container = createWith(null, "components-config.xml", coreServices, caches, roles);
return new WindsorServiceLocator(container);
}
internal static IEnumerable<IRegistration> coreServices()
{
yield return Component.For<ISystemClock>()
.ImplementedBy<PreciseSystemClock>()
.Parameters(Parameter.ForKey("synchronizePeriodSeconds").Eq("10"))
.LifeStyle.Singleton;
yield return Component.For<IMailService>()
.ImplementedBy<MailQueueService>()
.LifeStyle.Singleton;
}
internal static IEnumerable<IRegistration> caches()
{
yield return Component.For<IDataCache<ServiceAttributes>>()
.ImplementedBy<NoDataCache<ServiceAttributes>>()
.LifeStyle.Singleton;
// ....
}
}
进行接线的基类:(Logging 来自 Commons.Logging)
public class WindsorConfigurationSkeleton
{
private static readonly ILog _log = LogManager.GetLogger(
typeof(WindsorConfigurationSkeleton));
internal static IWindsorContainer createWith(
IRegistration[] customs, string configFile, params Func<IEnumerable<IRegistration>>[] methods)
{
IWindsorContainer container = new WindsorContainer();
BugFix.Kernel = container.Kernel;
container.AddFacility("factory.support", new FactorySupportFacility());
IList<Type> unnamedServices = new List<Type>();
IDictionary<string, Type> namedServices = new Dictionary<string, Type>();
ComponentDataDelegate registered = captureRegistrations(unnamedServices, namedServices);
container.Kernel.ComponentRegistered += registered;
if (customs != null)
{
container.Register(customs);
}
if (configFile != null)
{
tryAddXmlConfig(container, configFile);
}
container.Kernel.ComponentRegistered -= registered;
if (methods != null && methods.Length > 0)
{
container.Register(union(unnamedServices, namedServices, methods));
}
return container;
}
private static ComponentDataDelegate captureRegistrations(
IList<Type> unnamedServices, IDictionary<string, Type> namedServices)
{
return (key, handler) =>
{
if (handler.ComponentModel.Name == handler.ComponentModel.Implementation.FullName)
{
var text = unnamedServices.Contains(handler.Service) ? "another" : "default";
_log.Info(
m => m(
"Registered {2} service for {0} with {1}.",
handler.Service.GetDisplayName(),
handler.ComponentModel.Implementation.GetDisplayName(),
text
));
unnamedServices.Add(handler.Service);
}
else
{
var text = namedServices.ContainsKey(key) ? "another" : "default";
_log.Info(
m => m(
"Registered {3} service {0} with name '{1}' and {2}.",
handler.ComponentModel.Service,
handler.ComponentModel.Name,
handler.ComponentModel.Implementation.GetDisplayName(),
text
));
namedServices.Add(key, handler.Service);
}
};
}
protected static void tryAddXmlConfig(IWindsorContainer container, string filename)
{
var fi = Resources.GetFileFromResourceHierarchy(typeof(ApplicationContext).Namespace, filename);
if ( fi == null ) {
return;
}
var configFile = fi.FullName;
var xd = immitateFluentApiDefaultIdBehaviour(configFile);
container.Install(Configuration.FromXml(new StaticContentResource(xd.OuterXml)));
}
private static XmlDocument immitateFluentApiDefaultIdBehaviour(string configFile)
{
var xd = new XmlDocument();
xd.Load(configFile);
foreach (
XmlElement component in
xd.SelectNodes("/configuration/components/component[@type and (not(@id) or @id = '')]"))
{
var type = Type.GetType(component.GetAttribute("type"), true);
component.SetAttribute("id", type.FullName);
}
return xd;
}
private static IRegistration[] union(
IList<Type> unnamed, IDictionary<string, Type> named, params Func<IEnumerable<IRegistration>>[] methods)
{
var all = new List<IRegistration>();
foreach (var method in methods)
{
foreach (var registration in method())
{
var registrationType = registration.GetType();
if (registrationType.IsGenericTypeOf(typeof(ComponentRegistration<>)))
{
var componentType = registrationType.GetGenericArgumentsFor(typeof(ComponentRegistration<>))[0];
var name = (string)registrationType.GetProperty("Name").GetValue(registration, null);
if (name != null)
{
if (named.ContainsKey(name))
{
_log.Debug(
m => m("Skipped registering default named component {0}.", name));
continue;
}
}
else if (unnamed.Contains(componentType))
{
_log.Debug(
m => m("Skipped registering default component for type {0}.", componentType));
continue;
}
all.Add(registration);
}
else
{
all.Add(registration);
}
}
}
return all.ToArray();
}
}