开发者

How do I add a tracing interceptor for all classes within Prism?

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)


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜