Add ability to configure orchard components via config file

A special file "HostSingletons.config" is read at startup to
look for properties to set on host components.

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-12-12 22:40:39 -08:00
parent 01fdf07b95
commit b2d0f1d623
5 changed files with 155 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<HostComponents>
<Components>
<Component Type="Orchard.Environment.Extensions.Loaders.DynamicExtensionLoader">
<Properties>
<Property Name="Disabled" Value="true"/>
</Properties>
</Component>
</Components>
</HostComponents>

View File

@@ -122,6 +122,9 @@
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Content Include="Config\log4net.config" />
<Content Include="Config\Sample.HostComponents.config">
<SubType>Designer</SubType>
</Content>
<None Include="Media\web.config" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Autofac;
using Autofac.Core;
using Module = Autofac.Module;
namespace Orchard.Environment {
/// <summary>
/// Alter components instantiations by setting property values defined in a configuration file
/// </summary>
public class HostComponentsConfigModule : Module {
public static class XNames {
public const string Xmlns = "";
public static readonly XName HostComponents = XName.Get("HostComponents", Xmlns);
public static readonly XName Components = XName.Get("Components", Xmlns);
public static readonly XName Component = XName.Get("Component", Xmlns);
public static readonly XName Properties = XName.Get("Properties", Xmlns);
public static readonly XName Property = XName.Get("Property", Xmlns);
public static readonly XName Type = XName.Get("Type");
public static readonly XName Name = XName.Get("Name");
public static readonly XName Value = XName.Get("Value");
}
// component type name => list of [property name, property value]
public class PropertyEntry {
public string Name { get; set; }
public string Value { get; set; }
}
public readonly IDictionary<string, IEnumerable<PropertyEntry>> _config = new Dictionary<string, IEnumerable<PropertyEntry>>();
public HostComponentsConfigModule() {
// Called by the framework, as this class is a "Module"
}
public HostComponentsConfigModule(string fileName) {
var doc = XDocument.Load(fileName);
foreach (var component in doc.Elements(XNames.HostComponents).Elements(XNames.Components).Elements(XNames.Component)) {
var componentType = Attr(component, XNames.Type);
if (componentType == null)
continue;
var properties = component
.Elements(XNames.Properties)
.Elements(XNames.Property)
.Select(property => new PropertyEntry { Name = Attr(property, XNames.Name), Value = Attr(property, XNames.Value) })
.Where(t => !string.IsNullOrEmpty(t.Name) && !string.IsNullOrEmpty(t.Value))
.ToList();
if (!properties.Any())
continue;
_config.Add(componentType, properties);
}
}
private string Attr(XElement component, XName name) {
var attr = component.Attribute(name);
if (attr == null)
return null;
return attr.Value;
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
var implementationType = registration.Activator.LimitType;
IEnumerable<PropertyEntry> properties;
if (!_config.TryGetValue(implementationType.FullName, out properties))
return;
// build an array of actions on this type to assign loggers to member properties
var injectors = BuildPropertiesInjectors(implementationType, properties).ToArray();
// if there are no logger properties, there's no reason to hook the activated event
if (!injectors.Any())
return;
// otherwise, whan an instance of this component is activated, inject the loggers on the instance
registration.Activated += (s, e) => {
foreach (var injector in injectors)
injector(e.Context, e.Instance);
};
}
private IEnumerable<Action<IComponentContext, object>> BuildPropertiesInjectors(Type componentType, IEnumerable<PropertyEntry> properties) {
// Look for settable properties with name in "properties"
var settableProperties = componentType
.GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
.Select(p => new {
PropertyInfo = p,
IndexParameters = p.GetIndexParameters(),
Accessors = p.GetAccessors(false),
PropertyEntry = properties.Where(t => t.Name == p.Name).FirstOrDefault()
})
.Where(x => x.PropertyEntry != null) // Must be present in "properties"
.Where(x => x.IndexParameters.Count() == 0) // must not be an indexer
.Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set
// Return an array of actions that assign the property value
foreach (var entry in settableProperties) {
var propertyInfo = entry.PropertyInfo;
var propertyEntry = entry.PropertyEntry;
yield return (ctx, instance) => {
object value;
if (ChangeToCompatibleType(propertyEntry.Value, propertyInfo.PropertyType, out value))
propertyInfo.SetValue(instance, value, null);
};
}
}
public static bool ChangeToCompatibleType(string value, Type destinationType, out object result) {
if (string.IsNullOrEmpty(value)) {
result = null;
return false;
}
if (destinationType.IsAssignableFrom(value.GetType())) {
result = value;
return true;
}
try {
result = TypeDescriptor.GetConverter(destinationType).ConvertFrom(value);
return true;
}
catch {
result = null;
return false;
}
}
}
}

View File

@@ -108,6 +108,10 @@ namespace Orchard.Environment {
if (File.Exists(optionalHostConfig))
builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReader.DefaultSectionName, optionalHostConfig));
var optionalComponentsConfig = HostingEnvironment.MapPath("~/Config/HostComponents.config");
if (File.Exists(optionalComponentsConfig))
builder.RegisterModule(new HostComponentsConfigModule(optionalComponentsConfig));
var container = builder.Build();

View File

@@ -169,6 +169,7 @@
<Compile Include="Environment\Extensions\Loaders\RawThemeExtensionLoader.cs" />
<Compile Include="Environment\Features\FeatureManager.cs" />
<Compile Include="Environment\IAssemblyLoader.cs" />
<Compile Include="Environment\HostComponentsConfigModule.cs" />
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
<Compile Include="Environment\WorkContextImplementation.cs" />
<Compile Include="Environment\WorkContextModule.cs" />