如何为 Prism 中的所有类添加跟踪拦截器?

发布于 2024-09-27 21:34:22 字数 2634 浏览 3 评论 0原文

我正在尝试使用接口拦截器向 Prism 应用程序的所有组件添加跟踪拦截器。我几乎可以正常工作,但是拦截器在声明事件的接口方面存在问题。有没有人成功地实现了所有组件的 aop 跟踪而不需要属性?

这是我的代码:

private void AddTracingInterceptor(Type from, IUnityContainer container)
    {
        container.AddNewExtension<Interception>();
        if (from.ToString().StartsWith("StockTraderRI")
            && !from.ToString().EndsWith("View")
            && from.IsInterface)
        {
            try
            {
                container.Configure<Interception>().SetInterceptorFor(from, new InterfaceInterceptor())
                    .AddPolicy("SomePolicy")
                    .AddMatchingRule(new AllMembersMatchingRule())
                    .AddCallHandler(new TraceCallHandler());
            }
            catch (Exception ex)
            {
                Debug.WriteLine("$$$" + from.ToString() + " " + ex.Message);
            }
        }
        else
        {
            Debug.WriteLine("---" + from.ToString());
        }
    }

这是例外:

内部异常 ---------------- 类型:System.TypeLoadException,mscorlib,版本=2.0.0.0,文化=中性,PublicKeyToken=b77a5c561934e089 消息:程序集“Unity_ILEmit_InterfaceProxies,Version=0.0.0.0,Culture=neutral,PublicKeyToken=null”中的“DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1”类型中的方法“add_Updated”没有实现。 来源:mscorlib 帮助链接: 类型名称:DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1 数据:System.Collections.ListDictionaryInternal 目标站点:System.Type _TermCreateClass(Int32,System.Reflection.Module) 堆栈跟踪:位于 System.Reflection.Emit.TypeBuilder._TermCreateClass(Int32 句柄,模块模块) 在 System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() 在 System.Reflection.Emit.TypeBuilder.CreateType() 在 Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptorClassGenerator.CreateProxyType() 在 Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptor.CreateProxy(类型 t,对象目标) 在 Microsoft.Practices.Unity.InterceptionExtension.InstanceInterceptionStrategy.PostBuildUp(IBuilderContext 上下文) 在 Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext 上下文)

I am trying to add a tracing interceptor to all the components of my Prism application using an interface interceptor. I almost have this working however the interceptor is having problems with interfaces that declare an event. Has anyone successfully implemented aop tracing for all components without the need for attributes?

Here is my code:

private void AddTracingInterceptor(Type from, IUnityContainer container)
    {
        container.AddNewExtension<Interception>();
        if (from.ToString().StartsWith("StockTraderRI")
            && !from.ToString().EndsWith("View")
            && from.IsInterface)
        {
            try
            {
                container.Configure<Interception>().SetInterceptorFor(from, new InterfaceInterceptor())
                    .AddPolicy("SomePolicy")
                    .AddMatchingRule(new AllMembersMatchingRule())
                    .AddCallHandler(new TraceCallHandler());
            }
            catch (Exception ex)
            {
                Debug.WriteLine("$$" + from.ToString() + " " + ex.Message);
            }
        }
        else
        {
            Debug.WriteLine("---" + from.ToString());
        }
    }

and this is the exception:

Inner Exception
---------------
Type : System.TypeLoadException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : Method 'add_Updated' in type 'DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1' from assembly 'Unity_ILEmit_InterfaceProxies, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
Source : mscorlib
Help link :
TypeName : DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1
Data : System.Collections.ListDictionaryInternal
TargetSite : System.Type _TermCreateClass(Int32, System.Reflection.Module)
Stack Trace : at System.Reflection.Emit.TypeBuilder._TermCreateClass(Int32 handle, Module module)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateType()
at Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptorClassGenerator.CreateProxyType()
at Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptor.CreateProxy(Type t, Object target)
at Microsoft.Practices.Unity.InterceptionExtension.InstanceInterceptionStrategy.PostBuildUp(IBuilderContext context)
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

荆棘i 2024-10-04 21:34:22

查了一下,问题出在Unity InterceptionExtension中的InterfaceInterceptorClassGenerator上,它绕过了特殊名称的方法,并且没有定义事件所需的add和remove方法。

我能看到的可能的解决方案是
1)编辑Unity源代码,编写定义事件IL代码的代码。 (见下文)
2) 将接口中要拦截的所有事件更改为显式的 Add 和 Remove 委托方法(由真实事件实现)。 INotifyPropertyChanged 上的 WPF 绑定使得这对于 Prism 来说不切实际。
3)废弃Unity并使用更好的IoC容器。

您找到更好的解决问题的方法了吗?

编辑:我现在一直使用Unity 1.2,所以我最终修复了它,不妨发布代码,这也解决了派生接口的问题。

您需要修改 Unity.Extensions.Interception 中的 InterfaceInterceptorClassGenerator 类,首先添加 CreateProxyType

public Type CreateProxyType()
{
   int memberCount = 0;
   foreach (MethodInfo method in MethodsToIntercept())
   {
        OverrideMethod(method, memberCount++);
   }

   foreach (PropertyInfo property in PropertiesToIntercept())
   {
        OverrideProperty(property, memberCount++);
   }

   // Add this 
   foreach (EventInfo evt in EventsToIntercept())
   {
        AddEvent(evt);
   }

  // -- SNIP --
}

修改内容以获取“基本”接口的方法。

    private IEnumerable<MethodInfo> MethodsToIntercept()
    {
        return typeToIntercept.GetInterfaces()
            .SelectMany(t => t.GetMethods())
            .Union(typeToIntercept.GetMethods())
            .Where(m => !m.IsSpecialName);
    }

    private IEnumerable<PropertyInfo> PropertiesToIntercept()
    {
        return typeToIntercept.GetInterfaces()
            .SelectMany(t => t.GetProperties())
            .Union(typeToIntercept.GetProperties());
    }

    private IEnumerable<EventInfo> EventsToIntercept()
    {
        return typeToIntercept.GetInterfaces()
            .SelectMany(t => t.GetEvents())
            .Union(typeToIntercept.GetEvents());
    }

然后添加创建事件方法的方法。首先使用 在动态类型上实现接口中的代码带有事件,但实际上将添加/删除转发到底层对象:

private void AddEvent(EventInfo interfaceEvent)
{
    MethodAttributes eventMethodAttr = MethodAttributes.Public | MethodAttributes.HideBySig |             MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName;

    MethodImplAttributes eventMethodImpAtr = MethodImplAttributes.Managed | MethodImplAttributes.Synchronized;

    string qualifiedEventName = string.Format("{0}.{1}", typeToIntercept.Name, interfaceEvent.Name);
    string addMethodName = string.Format("add_{0}", interfaceEvent.Name);
    string remMethodName = string.Format("remove_{0}", interfaceEvent.Name);

    EventBuilder eBuilder = typeBuilder.DefineEvent(qualifiedEventName, EventAttributes.None, interfaceEvent.EventHandlerType);

    // ADD method
    MethodBuilder addMethodBuilder = typeBuilder.DefineMethod(addMethodName, eventMethodAttr, null, new[] { interfaceEvent.EventHandlerType }); 
    addMethodBuilder.SetImplementationFlags(eventMethodImpAtr);

    // Code generation      
    ILGenerator ilgen = addMethodBuilder.GetILGenerator();
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ldarg_0);             
    ilgen.Emit(OpCodes.Ldfld, targetField);
    ilgen.Emit(OpCodes.Ldarg_1);               
    ilgen.Emit(OpCodes.Callvirt, interfaceEvent.GetAddMethod());
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ret);

    // REMOVE method     
    MethodBuilder removeMethodBuilder = typeBuilder.DefineMethod(remMethodName, eventMethodAttr, null, new[] { interfaceEvent.EventHandlerType });
    removeMethodBuilder.SetImplementationFlags(eventMethodImpAtr);

    // Code generation     
    ilgen = removeMethodBuilder.GetILGenerator();
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldfld, targetField);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Callvirt, interfaceEvent.GetRemoveMethod());
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ret);

    // Finally, setting the AddOn and RemoveOn methods for our event    
    eBuilder.SetAddOnMethod(addMethodBuilder);     
    eBuilder.SetRemoveOnMethod(removeMethodBuilder);
}

如果您将索引器放在接口中,您可能还需要对索引器执行类似的操作,但只需修改接口以获取/设置方法就很容易了。

Looking into it the problem lies with the InterfaceInterceptorClassGenerator in the Unity InterceptionExtension, which bypasses methods with special names and does not define the add and remove methods needed for events.

The possible solutions I can see are
1) Edit the Unity source code and write the code to define the event IL code. (See below)
2) Change all events in interfaces that you want to intercept into explicit Add and Remove delegate methods (that are implemented by a real event). WPF binding on INotifyPropertyChanged makes this impractical for Prism.
3) Scrap Unity and use a better IoC container.

Did you find a better solution to the problem?

Edit: I'm stuck with Unity 1.2 for now so I've ended up fixing it and might as well post the code, which also fixes the issue of derived interfaces.

You'll need to modify the InterfaceInterceptorClassGenerator class in Unity.Extensions.Interception starting with an addition to CreateProxyType

public Type CreateProxyType()
{
   int memberCount = 0;
   foreach (MethodInfo method in MethodsToIntercept())
   {
        OverrideMethod(method, memberCount++);
   }

   foreach (PropertyInfo property in PropertiesToIntercept())
   {
        OverrideProperty(property, memberCount++);
   }

   // Add this 
   foreach (EventInfo evt in EventsToIntercept())
   {
        AddEvent(evt);
   }

  // -- SNIP --
}

Modify things to get the methods of 'base' interfaces.

    private IEnumerable<MethodInfo> MethodsToIntercept()
    {
        return typeToIntercept.GetInterfaces()
            .SelectMany(t => t.GetMethods())
            .Union(typeToIntercept.GetMethods())
            .Where(m => !m.IsSpecialName);
    }

    private IEnumerable<PropertyInfo> PropertiesToIntercept()
    {
        return typeToIntercept.GetInterfaces()
            .SelectMany(t => t.GetProperties())
            .Union(typeToIntercept.GetProperties());
    }

    private IEnumerable<EventInfo> EventsToIntercept()
    {
        return typeToIntercept.GetInterfaces()
            .SelectMany(t => t.GetEvents())
            .Union(typeToIntercept.GetEvents());
    }

Then add the method that creates the event methods. This started out using the code from Implementing an Interface on a dynamic type with events but actually forwards the add/remove to the underlying object:

private void AddEvent(EventInfo interfaceEvent)
{
    MethodAttributes eventMethodAttr = MethodAttributes.Public | MethodAttributes.HideBySig |             MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName;

    MethodImplAttributes eventMethodImpAtr = MethodImplAttributes.Managed | MethodImplAttributes.Synchronized;

    string qualifiedEventName = string.Format("{0}.{1}", typeToIntercept.Name, interfaceEvent.Name);
    string addMethodName = string.Format("add_{0}", interfaceEvent.Name);
    string remMethodName = string.Format("remove_{0}", interfaceEvent.Name);

    EventBuilder eBuilder = typeBuilder.DefineEvent(qualifiedEventName, EventAttributes.None, interfaceEvent.EventHandlerType);

    // ADD method
    MethodBuilder addMethodBuilder = typeBuilder.DefineMethod(addMethodName, eventMethodAttr, null, new[] { interfaceEvent.EventHandlerType }); 
    addMethodBuilder.SetImplementationFlags(eventMethodImpAtr);

    // Code generation      
    ILGenerator ilgen = addMethodBuilder.GetILGenerator();
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ldarg_0);             
    ilgen.Emit(OpCodes.Ldfld, targetField);
    ilgen.Emit(OpCodes.Ldarg_1);               
    ilgen.Emit(OpCodes.Callvirt, interfaceEvent.GetAddMethod());
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ret);

    // REMOVE method     
    MethodBuilder removeMethodBuilder = typeBuilder.DefineMethod(remMethodName, eventMethodAttr, null, new[] { interfaceEvent.EventHandlerType });
    removeMethodBuilder.SetImplementationFlags(eventMethodImpAtr);

    // Code generation     
    ilgen = removeMethodBuilder.GetILGenerator();
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldfld, targetField);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Callvirt, interfaceEvent.GetRemoveMethod());
    ilgen.Emit(OpCodes.Nop);
    ilgen.Emit(OpCodes.Ret);

    // Finally, setting the AddOn and RemoveOn methods for our event    
    eBuilder.SetAddOnMethod(addMethodBuilder);     
    eBuilder.SetRemoveOnMethod(removeMethodBuilder);
}

You might also need to do something similar for indexers if you have them in an interface but it's easy to just modify the interface to get/set methods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文