Как добавить отслеживающий перехватчик для всех классов в Prism? - PullRequest
1 голос
/ 16 октября 2010

Я пытаюсь добавить перехватчик трассировки ко всем компонентам моего приложения 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 Сообщение: метод add_Updated в типе DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8e7a88080из сборки 'Unity_ILEmit_InterfaceProxies, версия = 0.0.0.0, культура = нейтральная, PublicKeyToken = null' не имеет реализации.Источник: mscorlib Помощь ссылка: TypeName: DynamicModule.ns.Wrapped_IAccountPositionService_4c0175f8eca24b809f7a3875747d41c1 данных: System.Collections.ListDictionaryInternal TargetSite: System.Type _TermCreateClass (Int32, System.Reflection.Module) Трассировка стека: на System.Reflection.Emit.TypeBuilder._TermCreateClass (Int32дескриптор, модуль Module) в System.Reflection.Emit.TypeBuilder.CreateTypeNoLock () в System.Reflection.Emit.TypeBuilder.CreateType () в Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptorClassGenerator.CreateProeneT.InterceptionExtension.InterfaceInterceptor.CreateProxy (Тип t, Цель объекта) в Microsoft.Practices.Unity.InterceptionExtension.InstanceInterceptionStrategy.PostBuildUp (контекст IBuilderContext) в Microsoft.Practices.ObjectBuilder2.StrategyChain.ExptextBilder

1 Ответ

2 голосов
/ 04 января 2011

Глядя на это, проблема заключается в InterfaceInterceptorClassGenerator в Unity InterceptionExtension, который обходит методы со специальными именами и не определяет методы добавления и удаления, необходимые для событий.

Возможные решения, которые я вижу, это 1) Отредактируйте исходный код Unity и напишите код, чтобы определить код IL события.(См. Ниже) 2) Измените все события в интерфейсах, которые вы хотите перехватить, на явные методы делегирования Add и Remove (которые реализуются реальным событием).Привязка WPF к INotifyPropertyChanged делает это непрактичным для Prism.3) Удалите Unity и используйте лучший контейнер IoC.

Нашли ли вы лучшее решение проблемы?

Редактировать : я застрял с Unity 1.2 на данный моментпоэтому я в конечном итоге исправил его и мог бы также опубликовать код, который также устраняет проблему производных интерфейсов.

Вам нужно будет изменить класс InterfaceInterceptorClassGenerator в Unity.Extensions.Interception, начиная с добавления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 --
}

Измените вещи, чтобы получить методы «базовых» интерфейсов.

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

Возможно, вам также нужно что-то сделатьаналогично для индексаторов, если они есть в интерфейсе, но легко изменить интерфейс для получения / установки методов.

...