Отслеживание событий PRISM / CAL (лучшая практика?) - PullRequest
2 голосов
/ 07 октября 2009

Хорошо,

этот вопрос для людей с глубокими знаниями ПРИЗМЫ или с некоторыми магическими навыками, которых мне просто не хватает (пока).Фон прост: Prism позволяет объявлять события, на которые пользователь может подписаться или опубликовать.В коде это выглядит следующим образом:

  _eventAggregator.GetEvent<LayoutChangedEvent>().Subscribe(UpdateUi, true);
  _eventAggregator.GetEvent<LayoutChangedEvent>().Publish("Some argument");

Теперь это хорошо, особенно потому, что эти события строго типизированы, и объявление является простым куском:

public class LayoutChangedEvent : CompositePresentationEvent<string>
{
}

Но теперь приходитсамая сложная часть: я хочу каким-то образом отслеживать события.У меня была идея подписаться, используя лямбда-выражение, вызывающее простое сообщение журнала.Отлично работал в WPF, но в Silverlight есть какая-то ошибка доступа к методу (у меня ушло некоторое время, чтобы выяснить причину) .. Если вы хотите убедиться в этом, попробуйте это в Silverlight:

eA.GetEvent<VideoStartedEvent>().Subscribe(obj => TraceEvent(obj, "vSe", log));

Еслиэто было бы возможно, я был бы счастлив, потому что я мог легко отследить все события, используя одну строку для подписки.Но это не так ... Альтернативный подход - написание разных функций для каждого события и назначение этой функции событиям.Почему разные функции?Ну, мне нужно знать, КАКОЕ событие было опубликовано.Если я использую одну и ту же функцию для двух разных событий, я получаю только полезную нагрузку в качестве аргумента.Теперь у меня есть способ выяснить, какое событие вызвало сообщение трассировки.

Я попытался:

  • , используя Reflection, чтобы получить вызывающее событие (не работает)
  • , используяконструктор в событии, позволяющий каждому событию отслеживать себя (не разрешено)

Есть еще идеи?Крис

PS: Написание этого текста, скорее всего, заняло у меня больше времени, чем написание 20 функций для моих 20 событий, но я отказываюсь сдаваться :-) У меня была идея использовать postsharp, что, скорее всего, сработает (хотя я не уверен, возможно, у меня останется только информация о базовом классе) .. хитрая и столь неважная тема ...

Ответы [ 2 ]

5 голосов
/ 07 октября 2009

Вероятно, проще всего было бы создать подкласс CompositePresentationEvent и переопределить поведение события Publish. Вот источник для CompositePresentationEvent: http://compositewpf.codeplex.com/SourceControl/changeset/view/26112#496659

Вот текущее поведение публикации:

public virtual void Publish(TPayload payload)
{
     base.InternalPublish(payload);
}

Так что вы можете просто добавить немного к этому:

public virtual override void Publish(TPayload payload)
{
     ILoggerFacade logger = ServiceLocator.Current.GetInstance<ILoggerFacade>();
     logger.Log("Publishing " + payload.ToString(), Category.Debug, Priority.Low);
     base.InternalPublish(payload);
}

Здесь я использую средство ведения журнала, встроенное в Prism, но не стесняйтесь заменять свое собственное (или, лучше, просто внедрите ILoggerFacade!).

Я был удивлен тем, что в этой системе были опубликованы какие-либо сообщения по умолчанию или места для подключения трассировки ... так как EventAggregator злоупотребляет людьми, вы думаете, что это будет большой запрос!

1 голос
/ 28 марта 2010

Немного поздно, но лучше поздно, чем никогда! У меня недавно была такая же проблема, и вот как я ее решил.

Во-первых, мне не понравился метод публикации / подписки на события Prism, поэтому вместо этого я использовал такой метод: http://neverindoubtnet.blogspot.com/2009/07/simplify-prism-event-aggregator.html

В этом посте выше предлагается использовать методы Extension в Event Aggregator, чтобы упростить вызов публикации / подписки. В результате ваш клиентский код выглядит так:

IEventAggregator ev;
ev.Publish<MyCustomMessage>();

//or

ev.Publish(new MyCustomMessage(someData));

//and similarly subscription

ev.Subscribe<MyCustomMessage(this.OnCustomMessageReceived);

// ... 

private void OnCustomMessageReceived(MyCustomMessage message)
{
   // ...
}

// With a BaseMessageEvent class as follows (see the blog post above for where this comes from)

/// <summary>
/// Base class for all messages (events) 
/// </summary>
/// <typeparam name="TMessage">The message type (payload delivered to subscribers)</typeparam>
public class BaseEventMessage<TMessage> : CompositePresentationEvent<TMessage>
{
}

Хорошо, это здорово, но вместо хакерских методов расширения я реализовал свой собственный сервис событий следующим образом:

/// <summary>
/// The EventService instance
/// </summary>
public class EventService : IEventService
{
    private readonly IEventAggregator eventAggregator;
    private readonly ILoggerFacade logger;

    /// <summary>
    /// Initializes a new instance of the <see cref="EventService"/> class.
    /// </summary>
    /// <param name="logger">The logger instance.</param>
    /// <param name="eventAggregator">The event aggregator instance.</param>
    public EventService(IEventAggregator eventAggregator, ILoggerFacade logger)
    {
        this.logger = logger;
        this.eventAggregator = eventAggregator;
    }

    #region IEventService Members

    /// <summary>
    /// Publishes the event of type TMessageType to all subscribers
    /// </summary>
    /// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
    public void Publish<TMessageType>() where TMessageType : BaseEventMessage<TMessageType>, new()
    {
        TMessageType message = Activator.CreateInstance<TMessageType>();

        this.Publish(message);
    }

    /// <summary>
    /// Publishes the event of type TMessageType to all subscribers
    /// </summary>
    /// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
    /// <param name="message">The message to publish</param>
    public void Publish<TMessageType>(TMessageType message) where TMessageType : BaseEventMessage<TMessageType>, new()
    {
        // Here we can log our message publications
        if (this.logger != null)
        {
           // logger.log etc.. 
        }
        this.eventAggregator.GetEvent<TMessageType>().Publish(message);
    }

    /// <summary>
    /// Subscribes to the event of type TMessage
    /// </summary>
    /// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
    /// <param name="action">The action to execute when the event is raised</param>
    public void Subscribe<TMessageType>(Action<TMessageType> action) where TMessageType : BaseEventMessage<TMessageType>, new()
    {
        // Here we can log our message publications
        if (this.logger != null)
        {
           // logger.log etc.. 
        }
        this.eventAggregator.GetEvent<TMessageType>().Subscribe(action);
    }

    #endregion
}

Затем я регистрирую IEventService / EventService в качестве синглтона в загрузчике и забываю об использовании IEventAggregator, просто используйте это (однако, если кто-то использует IEventAggregator, его тот же экземпляр, что и EventService, будет работать).

Наконец, еще один трюк, который нужно добавить, это использование стекового фрейма, чтобы сообщить мне, откуда поступают публикации и подписки. Обратите внимание, что это медленный процесс (разматывание кадра стека), поэтому используйте его с осторожностью. Если ты регулярно вызывая событие, возможно, поместите флаг в BaseEventMessage и проверьте, чтобы увидеть, нужно ли регистрировать публикации для определенных типов событий.

// Inside Publish method ... Log the subscription
if (this.logger != null)
{
   Type messageType = typeof(TMessageType);
   Type callingType = GetCallingType();
   string methodName = GetCallingMethod().Name;

   // Log the publication of this event
   this.logger.Log(
         string.Format("Event {0} was published by {1}.{2}()", 
            messageType.Name,
            callingType.Name,
            methodName),
      Category.Debug, 
      Priority.Low));
}

// Additional methods to add to EventService to get the calling type/class
//

/// <summary>
/// Gets the Type that called the method or property where GetCallingType is called
/// </summary>
/// <returns>The class type that called</returns>
[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static Type GetCallingType()
{
    int skip = 2;
    MethodBase method = new StackFrame(skip, false).GetMethod();
    return method.DeclaringType;
}

/// <summary>
/// Gets the Method that called the method or property where GetCallingMethod is called
/// </summary>
/// <returns>The method type that was called</returns>
public static MethodBase GetCallingMethod()
{
    return new StackFrame(2, false).GetMethod();
}

Обратите внимание, что вышеописанное не будет работать в Silverlight (использование StackFrame), но остальное работает. Я нашел это бесценным при отладке множества событий, летающих вокруг приложения Prism!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...