Contents
In my last article, I showed how one could "extend" a framework using custom type descriptors. Unfortunately, the modifications you make using this mechanism are only visible through TypeDescriptor's inspection since they are not real. In this article, I'm going to show how one could really extend a framework with Castle MicroKernel/DynamicProxy for Inversion of Control.
Castle DynamicProxy was created with a very specific task: Aspect Oriented Programming through method interception. Its developers didn't create it to allow third party extensions. However, Castle Project libraries are so well designed and loosely coupled, that after downloading and examining its source codes, it was not hard for me to find some "unintended" extension points that would allow me to not only modify the proxies generated, but to integrate this new functionality with Castle Microkernel/Windsor.
The following topics will be covered in this article:
- Extending Castle DynamicProxy to allow one to plug in contributors (
ITypeContributor
objects) that make modifications to the dynamic type generated. - Integrating this functionality with Microkernel/Windsor, allowing consumers to specify their own
ITypeContributor
s to the ComponentModel. - Building a Microkernel 'facility', a class whose primary purpose is to extend or integrate Microkernel with another framework. For this, I'm going to show a facility that integrates the PresentationModel framework with Microkernel.
- Show how these modifications affected the demo application in my last article.
Target audience
This article is for people who are interested in Inversion of Control, or in extending an existing framework at runtime. I will also show a more advanced usage of Microkernel/Windsor, so it might be interesting to users of this container. It is expected that the reader has some knowledge with creating proxies, either with Castle's libraries, or with any other framework. Here is a great tutorial for understanding what proxies are and learning how to use Castle DynamicProxy.
To manually create a proxy with Castle's DynamicProxy, one does have to instantiate the ProxyGenerator
class, then call one of its many methods, optionally passing options or interceptors:
ProxyGenerator generator = new ProxyGenerator();
var proxy = generator.CreateClassProxy<SomeType<(interceptor1, interceptor2);
The ProxyGenerator
class delegates the job to an internal service, the IProxyBuilder
, and that's why it has an overload to the constructor:
ProxyGenerator generator = new ProxyGenerator(someIProxyBuilderObject);
If no IProxyBuilder
is passed, it will use a default implementation, the DefaultProxyBuilder
. The DefaultProxyBuilder
itself will only choose an appropriate class for building a specific type of proxy (there are interface/class proxies, with or without target). After looking into each generator class code, I noticed that at some point, it passes the dynamic type being emitted to 'ITypeContributor
' objects for processing. Here is the contract:
public interface ITypeContributor
{
void CollectElementsToProxy(IProxyGenerationHook hook, MetaType model);
void Generate(ClassEmitter @class, ProxyGenerationOptions options);
}
The ClassEmitter
exposes the System.Reflection.Emit.TypeBuilder
in the TypeBuilder
property. So each ITypeContributor
can make any modifications to the dynamic type. The problem is that there is no built-in way to insert a custom ITypeContributor
for processing; the contributor's collection is created inside a method that each of the generator class has. Here is the signature for the method inside the ClassProxyGenerator
class:
protected virtual IEnumerable<Type> GetTypeImplementerMapping(Type[] interfaces,
out IEnumerable<ITypeContributor> contributors, INamingScope namingScope);
It returns the contributors as out
parameters. Luckily, it's a virtual method, and so it's possible to insert more contributors if this class is extended. Since each of the specialized proxy generating classes has its own version of the method, all of them have to be extended:
protected override IEnumerable<Type> GetTypeImplementerMapping(Type[] interfaces,
out IEnumerable<ITypeContributor> contributors, INamingScope namingScope)
{
IEnumerable<ITypeContributor> contr;
var ret = base.GetTypeImplementerMapping(interfaces, out contr, namingScope);
var list = contr.ToList();
list.AddRange(_proxyBuilder.Contributors);
contributors = list;
return ret;
}
The _proxyBuilder
field is a reference to the custom ProxyBuilder I had to implement. Unfortunately, the DefaultProxyBuilder
class does not offer any extension points, so I had to build one from the scratch. Basically, I copied the code from the DefaultProxyBuilder
and got to this:
public class ExtensibleProxyBuilder : IProxyBuilder
{
private ILogger _logger = NullLogger.Instance;
private readonly ModuleScope _scope;
public ExtensibleProxyBuilder(params ITypeContributor[] contributors)
: this(new ModuleScope(), contributors)
{
}
public ExtensibleProxyBuilder(ModuleScope scope,
params ITypeContributor[] contributors)
{
this._scope = scope;
foreach (var item in contributors)
{
_contributors.Add(item);
}
}
private List<ITypeContributor> _contributors = new List<ITypeContributor>();
public List<ITypeContributor> Contributors
{
get { return _contributors; }
}
public Type CreateClassProxyType(Type classToProxy,
Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
{
AssertValidType(classToProxy);
AssertValidTypes(additionalInterfacesToProxy);
var generator = new ExtensibleClassProxyGenerator(_scope,
classToProxy, this) { Logger = _logger };
return generator.GenerateCode(additionalInterfacesToProxy, options);
}
public Type CreateInterfaceProxyTypeWithTarget(Type interfaceToProxy,
Type[] additionalInterfacesToProxy, Type targetType,
ProxyGenerationOptions options)
{
AssertValidType(interfaceToProxy);
AssertValidTypes(additionalInterfacesToProxy);
var generator = new ExtensibleInterfaceProxyWithTargetGenerator(
_scope, interfaceToProxy, this) { Logger = _logger };
return generator.GenerateCode(targetType, additionalInterfacesToProxy, options);
}
public Type CreateInterfaceProxyTypeWithTargetInterface(Type interfaceToProxy,
Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
{
AssertValidType(interfaceToProxy);
AssertValidTypes(additionalInterfacesToProxy);
var generator = new ExtensibleInterfaceProxyWithTargetInterfaceGenerator(
_scope, interfaceToProxy, this) { Logger = _logger };
return generator.GenerateCode(interfaceToProxy, additionalInterfacesToProxy, options);
}
public Type CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy,
Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
{
AssertValidType(interfaceToProxy);
AssertValidTypes(additionalInterfacesToProxy);
var generator = new InterfaceProxyWithoutTargetGenerator(_scope,
interfaceToProxy) { Logger = _logger };
return generator.GenerateCode(typeof(object), additionalInterfacesToProxy, options);
}
}
This class has other methods, but the principal are these. This is pretty much a copy from the DefaultProxyBuilder
, except it has a list to insert a custom user defined ITypeContributor
, and creates the specialized proxy generator classes that I extended. Now it is easy to make modifications to the proxy:
ExtensibleProxyBuilder builder = new ExtensibleProxyBuilder(tcontributor1, tcontributor2);
ProxyGenerator generator = new ProxyGenerator(builder);
Integrating with Microkernel
By default, Microkernel does not create proxies of its components, even if interceptors are added to the ComponentModel. There is a property of type IProxyFactory
in Microkernel/Windsor that is responsible to create the proxies when needed; here is the contract:
public interface IProxyFactory
{
void AddInterceptorSelector(IModelInterceptorsSelector selector);
object Create(IKernel kernel, object instance, ComponentModel model,
CreationContext context, params object[] constructorArguments);
bool RequiresTargetInstance(IKernel kernel, ComponentModel model);
bool ShouldCreateProxy(ComponentModel model);
}
The difference between Microkernel and Windsor is that Microkernel has a dummy implementation of this interface that always returns false
in the ShouldCreateProxy
method. To integrate this new ExtensibleProxyBuilder
with Microkernel/Windsor, we need to create a new IProxyFactory
. The DefaultProxyFactory
has only one method that allows us to customize the proxy object, but since the type was already created, it will do no good. And since it does not expose the ShouldCreateProxy
as virtual
, the only option is to implement the IProxyFactory
from scratch. Again, this was a copy and paste operation, with some minor but important modifications in the methods Create
and ShouldCreateProxy
:
public object Create(IKernel kernel, object target, ComponentModel model,
CreationContext context, params object[] constructorArguments)
{
ExtensibleProxyBuilder proxyBuilder;
if (model.ExtendedProperties.Contains(ExtensibleProxyConstants.ProxyTypeContributorsKey))
proxyBuilder = new ExtensibleProxyBuilder(_moduleScope,
(model.ExtendedProperties[ExtensibleProxyConstants.ProxyTypeContributorsKey]
as List<ITypeContributor>).ToArray());
else
proxyBuilder = new ExtensibleProxyBuilder(_moduleScope);
ProxyGenerator generator = new ProxyGenerator(proxyBuilder);
}
public bool ShouldCreateProxy(ComponentModel model)
{
if (model.ExtendedProperties.Contains(ExtensibleProxyConstants.ProxyTypeContributorsKey))
return true;
}
That's all that is needed to integrate this Extensible DynamicProxy with Microkernel. Now I'm going to show how to apply this on the sample from my last article.
On my last sample, I created a configuration assembly that contained all the code to register components in Windsor. Most of that code was related to integrating the PresentationModel framework (which is independent of any UI engine) to WPF. This time, I moved all that configuration to an assembly that contains a specialized facility (Microkernel extension). The difference is that in this facility, I use classes that implement IContributeComponentModelConstruction
to modify the component model as it is registered. I also removed all custom type descriptors, and replaced them for implementations of the ITypeContributor
interface. Here is a sample IContributeComponentModelConstruction
with its correspondent ITypeContributor
:
public class IsWorkingPropertyComponentContributor : IContributeComponentModelConstruction
{
public void ProcessModel(IKernel kernel, ComponentModel model)
{
if (typeof(IPresentationModel).IsAssignableFrom(model.Implementation))
{
var contributors = model.GetTypeContributors();
contributors.Add(new NotificatingPropertyTypeContributor(
"IsWorking", typeof(bool)));
}
}
}
public class NotificatingPropertyTypeContributor : ITypeContributor
{
public NotificatingPropertyTypeContributor(string propertyName, Type propertyType)
{
_propertyName = propertyName;
_propertyType = propertyType;
}
string _propertyName;
Type _propertyType;
public void CollectElementsToProxy(IProxyGenerationHook hook, MetaType model)
{
}
public void Generate(ClassEmitter @class, ProxyGenerationOptions options)
{
GenerateSimpleProperty(@class.TypeBuilder);
}
protected virtual void GenerateSimpleProperty(TypeBuilder typeBuilder)
{
var fieldBuilder = typeBuilder.DefineField(
string.Format("_{0}", _propertyName),
_propertyType,
FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(
_propertyName,
PropertyAttributes.None,
_propertyType,
Type.EmptyTypes);
var getterBuilder = typeBuilder.DefineMethod(
string.Format("get_{0}", _propertyName),
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName,
_propertyType,
Type.EmptyTypes);
var getterGenerator = getterBuilder.GetILGenerator();
getterGenerator.Emit(OpCodes.Ldarg_0);
getterGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
getterGenerator.Emit(OpCodes.Ret);
var setterBuilder = typeBuilder.DefineMethod(
string.Format("set_{0}", _propertyName),
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName,
null,
new Type[] { _propertyType });
var setterGenerator = setterBuilder.GetILGenerator();
Label returnInstruction = setterGenerator.DefineLabel();
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
setterGenerator.Emit(OpCodes.Ldarg_1);
setterGenerator.Emit(OpCodes.Ceq);
setterGenerator.Emit(OpCodes.Brtrue, returnInstruction);
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldarg_1);
setterGenerator.Emit(OpCodes.Stfld, fieldBuilder);
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldstr, _propertyName);
setterGenerator.Emit(OpCodes.Callvirt,
typeBuilder.BaseType.GetMethod("OnPropertyChanged",
BindingFlags.NonPublic | BindingFlags.Instance));
setterGenerator.MarkLabel(returnInstruction);
setterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);
}
}
The NotificatingPropertyTypeContributor
class allows the user to add a simple property that raises change notifications on the PresentationModel. Some people may be uncomfortable in programming against a stack machine, but know that in .NET 4.0, you can write dynamic methods using Expression Trees. I did this in CIL to make this sample compatible with .NET 3.5. The IsWorking
contributor creates a property with similar behavior to this:
private bool _isWorking;
public virtual bool IsWorking
{
get { return _isWorking; }
set
{
if (_isWorking == value)
return;
_isWorking = value;
OnPropertyChanged("IsWorking");
}
}
The CallPropertyTypeContributor
adds this property to the PresentationModels:
private MethodCallCommand _call
public MethodCallCommand Call
{
get
{
if (_call == null)
_call = new MethodCallCommand(this);
return _call;
}
}
All interceptors and ITypeContributor
implementations will be added to the ComponentModel by the IContributeComponentModelConstruction
implementations, which in turn will be added to the Microkernel facility. This facility is reusable in any PresentationModel/WPF project:
public class PresentationModelWpfFacility : AbstractFacility
{
protected override void Init()
{
if (!typeof(ExtensibleProxyFactory).IsAssignableFrom(
Kernel.ProxyFactory.GetType()))
Kernel.ProxyFactory = new ExtensibleProxyFactory();
HandleContributors();
RegisterComponents();
}
protected void HandleContributors()
{
var propertyDIContributor =
Kernel.ComponentModelBuilder.Contributors.OfType<
PropertiesDependenciesModelInspector>().Single();
Kernel.ComponentModelBuilder.RemoveContributor(propertyDIContributor);
Kernel.ComponentModelBuilder.AddContributor(
new CallPropertyComponentContributor());
Kernel.ComponentModelBuilder.AddContributor(new
EntityViewEncapsulationPropertiesComponentContributor());
Kernel.ComponentModelBuilder.AddContributor(
new IsWorkingPropertyComponentContributor());
Kernel.ComponentModelBuilder.AddContributor(
new AutomaticThreadingComponentContributor());
}
protected void RegisterComponents()
{
Kernel.AddComponentInstance("presentationmodel.proxyoptions",
new ProxyOptions() { Hook = new ThreadingProxyHook() });
Kernel.Register
(
Component.For(typeof(ObservableCollection<>),
typeof(ICollection<>)).LifeStyle.Is(LifestyleType.Transient),
Component.For<WpfDialogSystem, IDialogSystem>(),
Component.For<CustomActionPresentationModel,
ICustomActionPresentationModel>().LifeStyle.Is(LifestyleType.Transient),
Component.For<DispatchInterceptor>(),
Component.For<BackgroundWorkInterceptor>()
);
}
}
I also did make changes to the application core and framework. Firstly, I removed the EntityViewPresentationModel<T>
class. Now there is only the IEntityViewPresentationModel<T>>
interface:
public interface IEntityViewPresentationModel<T> : ISelectableViewPresentationModel
where T : class, new()
{
T Entity { get; set; }
}
This interface has a simple implementation, and a PresentationModel can now implement this interface multiple times if it wishes to display the properties of more than one entity type. That's why I made a change to the EncapsulatesPropertyAttribute
:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class EncapsulatesPropertyAttribute : Attribute
{
public EncapsulatesPropertyAttribute(Type entityType, string propertyName)
{
_entityType = entityType;
_propertyName = propertyName;
}
private string _propertyName;
public string PropertyName
{
get { return _propertyName; }
set { _propertyName = value; }
}
private Type _entityType;
public Type EntityType
{
get { return _entityType; }
set { _entityType = value; }
}
}
This allows a PresentationModel to encapsulate properties of many entities at once. Here is the code for EntityViewEncapsulationPropertiesComponentContributor
:
public class EntityViewEncapsulationPropertiesComponentContributor :
IContributeComponentModelConstruction
{
public void ProcessModel(IKernel kernel, ComponentModel model)
{
var contractDefiniton = typeof(IEntityViewPresentationModel<>);
var entityViews = model.Implementation.GetInterfaces()
.Where(t => t.IsGenericType &&
t.GetGenericTypeDefinition() == contractDefiniton);
if (entityViews.Count() > 0)
{
foreach (var entityView in entityViews)
{
var entityType = entityView.GetGenericArguments()[0];
var contributors = model.GetTypeContributors();
var propertiesToBeEncapsulated =
model.Implementation.GetCustomAttributes(
typeof(EncapsulatesPropertyAttribute), true)
.OfType<EncapsulatesPropertyAttribute>()
.Where(attr => attr.EntityType == entityType);
foreach (var encapsulatedProperty in propertiesToBeEncapsulated)
{
PropertyInfo pi =
entityType.GetProperty(encapsulatedProperty.PropertyName);
if (pi == null)
continue;
var typeContributor =
new EntityViewEncapsulationPropertyTypeContributor(
pi.Name,
entityType,
pi.PropertyType,
model.Implementation);
contributors.Add(typeContributor);
}
}
}
}
}
In simple terms, this will look for each interface implemented by the PresentationModel that is a constructed generic interface of the IEntityViewPresentationModel<>
generic interface definition. For each of these interfaces, it will construct properties that encapsulate the properties specified by the EncapsulatesPropertyAttribute
that are tied with the type parameter of the generic interface. Now, here is the new configuration class:
public class ConfigurationManager
{
internal static DefaultKernel _microkernel = new DefaultKernel();
public static void Configure()
{
AppDomain.CurrentDomain.SetData("servicelocator", _microkernel);
_microkernel.AddFacility<PresentationModelWpfFacility>();
_microkernel.Register
(
Component.For<MainViewPresentationModel,
IMainViewPresentationModel>().
LifeStyle.Is(LifestyleType.Singleton),
Component.For<ProductsViewPresentationModel,
IEntityCollectionViewPresentationModel<Product>>().
LifeStyle.Is(LifestyleType.Singleton),
Component.For(typeof(DummyDao<>), typeof(IDao<>)).
LifeStyle.Is(LifestyleType.Singleton),
Component.For(typeof(List<>), typeof(IList<>)).
LifeStyle.Is(LifestyleType.Transient),
Component.For<ProductEditViewPresentationModel,
IEntityViewPresentationModel<Product>>().
LifeStyle.Is(LifestyleType.Transient)
);
InsertStubData();
}
}
Much simpler, right? Notice that I replaced Windsor with Microkernel. Although Windsor has more features than Microkernel, the DefaultProxyFactory
was the only Windsor-exclusive feature being used . Since I created a new IProxyFactory
implementation that is independent of Windsor, I'd rather not create unnecessary dependencies.
As shown in this article, with dynamic proxies and Inversion of Control, it's possible to integrate frameworks that are unaware of each other. It is also possible to greatly reduce the amount of repetitive code by generating it dynamically. What I have shown here is not 1% of the possibilities a programmer can achieve by building types at runtime. It would, for example, be possible to generate specialized ICommand
implementations as nested types on each PresentationModel proxy (these ICommand
s would be created based on the methods contained in the PresentationModel).
I hope you liked what I showed here; please leave your comments/suggestions so I can improve my future articles.