In StructureMap we can proxy TInterface
and TConcreteImpl
with TProxy
this this:
ConfigurationExpression config = ...
config.For<TInterface>().DecorateAllWith<TProxy>();
config.For<TInterface>().Use<TConcreteImpl>();
I wanted to use DispatchProxy
(and globally log before method invocation and after invocation) and globally register it for all types being instantiated from StructureMap, I'm wondering how to accomplish this?
More specifically, I want to run the following for all the types being instantiated:
TConcreteImpl instance = ...
TInterface proxy = DispatchProxyGenerator.CreateProxyInstance(typeof (TInterface), typeof (TProxy))
.SetParameters(instance);
I already experimented with IInstancePolicy
of StructureMap but no success because Instance
is not the actual object instance.
public class Policy : IInstancePolicy
{
public void Apply(Type pluginType, Instance instance)
{
}
}
Thank you so much
Looks like implementing a custom IInterceptorPolicy fits here. It will be called for all types in the container, and may produce decorators for some/all of them. An example of it with dummy logger to console:
public class CustomInterception : IInterceptorPolicy
{
public string Description => "test interception Console.WriteLine each method' arguments and return value";
public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, StructureMap.Pipeline.Instance instance)
{
Type dispatchProxyType = DummyDispatchProxyDontUseAtWork.GenerateStructureMapCompatibleDispatchProxyType(pluginType);
yield return new DecoratorInterceptor(pluginType, dispatchProxyType);
}
}
Called as:
var container = new StructureMap.Container(cntnr =>
{
cntnr.Policies.Interceptors(new CustomInterception());
cntnr.For<IFoo>().Use<Foo>();
cntnr.For<IBar>().Use<Bar>();
});
var foo = container.GetInstance<IFoo>();
foo.FooFoo("1", "2");
Produced output:
FooFoo(1,2)
BarBar(2,1)
BarBar -> 21
FooFoo -> 21
The rest of example is below, so it could be executed.
Tricky thing with DispatchProxy
is that it creates a new type which is hard to be constructed by StructureMap
. In .Net Core 2.1
DispatchProxy
makes constructor with Action...
parameter, but StructureMap
expects something it can create. You definitely need an alternative proxy generator which would work with StructureMap
more smoothly.
public interface IBar
{
string BarBar(string a1, string a2);
}
public class Bar : IBar
{
public string BarBar(string a1, string a2) => a1 + a2;
}
public interface IFoo
{
string FooFoo(string a1, string a2);
}
public class Foo : IFoo
{
public IBar Bar { get; private set; }
public Foo(IBar bar)
{
Bar = bar;
}
public string FooFoo(string a1, string a2) => Bar.BarBar(a2, a1);
}
public class DummyDispatchProxyDontUseAtWork : DispatchProxy
{
public object Instance { get; protected set; }
public DummyDispatchProxyDontUseAtWork() : base()
{}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
Console.WriteLine($"{targetMethod.Name}({string.Join(',', args)})");
var result = targetMethod.Invoke(this.Instance, args);
Console.WriteLine($" {targetMethod.Name} -> {result}");
return result;
}
private static readonly ConcurrentDictionary<Type, Type> generatedProxyTypes = new ConcurrentDictionary<Type, Type>();
protected static readonly ConcurrentDictionary<string, object> privateHackedState = new ConcurrentDictionary<string, object>();
private static readonly AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
private static readonly ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(Guid.NewGuid().ToString());
private static Type EmitDispatchProxyType(Type interfaceType)
{
object dispatchProxyObj = typeof(DispatchProxy).GetMethod("Create", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(interfaceType, typeof(DummyDispatchProxyDontUseAtWork))
.Invoke(null, null);
string typeId = "DummyDispatchProxyDontUseAtWork" + Guid.NewGuid().ToString("N");
privateHackedState[typeId] =
dispatchProxyObj.GetType().GetField("invoke", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dispatchProxyObj);
var resultTypeBuilder = moduleBuilder.DefineType(
typeId,
TypeAttributes.Public,
dispatchProxyObj.GetType());
var baseCtor = dispatchProxyObj.GetType().GetConstructors().First();
var ctor = resultTypeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new[] {interfaceType});
var il = ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldsfld, typeof(DummyDispatchProxyDontUseAtWork).GetField(nameof(privateHackedState), BindingFlags.NonPublic | BindingFlags.Static));
il.Emit(OpCodes.Ldstr, typeId);
il.Emit(OpCodes.Callvirt, typeof(ConcurrentDictionary<,>).MakeGenericType(typeof(string), typeof(object)).GetMethod("get_Item"));
il.Emit(OpCodes.Call, baseCtor);
var setInstanceMethodInfo = dispatchProxyObj.GetType()
.GetMethod("set_" + nameof(Instance),BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, setInstanceMethodInfo);
il.Emit(OpCodes.Ret);
return resultTypeBuilder.CreateType();
}
public static Type GenerateStructureMapCompatibleDispatchProxyType(Type interfaceType)
{
return generatedProxyTypes.GetOrAdd(interfaceType, EmitDispatchProxyType);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With