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

时间:2010-10-16 10:54:26

标签: c# unity-container prism aop

我正在尝试使用接口拦截器向我的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,Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089                                 消息:来自程序集'Unity_ILEmit_InterfaceProxies,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'的类型'DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1'中的方法'add_Updated'没有实现。                                 资料来源:mscorlib                                 帮助链接:                                 TypeName:DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1                                 数据:System.Collections.ListDictionaryInternal                                 TargetSite: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(Type t,Object target)                                    在Microsoft.Practices.Unity.InterceptionExtension.InstanceInterceptionStrategy.PostBuildUp(IBuilderContext context)                                    在Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)

1 个答案:

答案 0 :(得分:2)

调查问题在于Unity InterceptionExtension中的InterfaceInterceptorClassGenerator,它绕过具有特殊名称的方法,并且没有定义事件所需的添加和删除方法。

我能看到的可能解决方案是 1)编辑Unity源代码并编写代码以定义事件IL代码。 (见下文) 2)将要拦截的接口中的所有事件更改为显式的“添加和删除”委托方法(由实际事件实现)。对INotifyPropertyChanged的WPF绑定使得这对Prism来说是不切实际的。 3)Scrap 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());
    }

然后添加创建事件方法的方法。这开始使用Implementing an Interface on a dynamic type with events中的代码,但实际上将添加/删除转发给基础对象:

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

如果您在索引中使用它们,您可能还需要为索引器执行类似的操作,但只需将接口修改为get / set方法就很容易。