在返回之前,StructureMap代理所有实例或修改实例

时间:2019-07-17 14:32:27

标签: c# .net-core structuremap

在StructureMap中,我们可以使用TInterface来代理TConcreteImplTProxy

ConfigurationExpression config = ...

config.For<TInterface().DecorateAllWith<TProxy>();

config.For<TInterface>().Use<TConcreteImpl>();

我想使用DispatchProxy(并在方法调用之前和调用之后全局记录),并为从StructureMap实例化的所有类型全局注册它,我想知道如何实现此目的?

更具体地说,我要对要实例化的所有类型运行以下命令:

TConcreteImpl instance = ...

TInterface proxy = DispatchProxyGenerator.CreateProxyInstance(typeof (TInterface), typeof (TProxy))
     .SetParameters(instance);

我已经尝试过使用StructureMap的IInstancePolicy,但没有成功,因为Instance不是实际的对象实例。

public class Policy : IInstancePolicy
{
    public void Apply(Type pluginType, Instance instance)
    {

    }
}

非常感谢您

1 个答案:

答案 0 :(得分:5)

看起来像实现自定义IInterceptorPolicy一样适合这里。容器中的所有类型都将调用此方法,并且可能会为某些/全部类型产生装饰器。 使用虚拟记录器进行控制台的示例:

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);
    }
}

称为:

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");

产生的输出:

FooFoo(1,2)
BarBar(2,1)
   BarBar -> 21
   FooFoo -> 21

其余示例在下面,因此可以执行。 DispatchProxy的棘手之处在于它创建了一种新类型,而StructureMap很难构造这种新类型。在.Net Core 2.1中,DispatchProxy使构造函数具有Action...参数,但是StructureMap希望可以创建一些东西。您肯定需要一个替代的代理生成器,该代理生成器可以更顺利地与StructureMap一起使用。

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);
    }
}