首页 > 解决方案 > StructureMap 在返回之前代理所有实例或修改实例

问题描述

在 StructureMap 中,我们可以TInterface使用TConcreteImpl以下代理TProxy

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

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

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

    }
}

太感谢了

标签: c#.net-corestructuremap

解决方案


看起来像在这里实现自定义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. In.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);
    }
}

推荐阅读