跟踪PRISM / CAL事件(最佳实践?)

时间:2009-10-07 00:24:50

标签: c# events prism

确定,

这个问题适用于对PRISM有深入了解的人或者我缺乏的一些魔法技能。背景很简单:Prism允许声明用户可以订阅或发布的事件。在代码中,这看起来像这样:

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

现在这很好,特别是因为这些事件是强类型的,声明是小菜一碟:

public class LayoutChangedEvent : CompositePresentationEvent<string>
{
}

但现在遇到困难的部分:我想以某种方式追踪事件。我有想法使用lambda表达式订阅调用简单的日志消息。在WPF中工作得很好,但在Silverlight中有一些方法访问错误(花了我一些时间来弄清楚原因)..如果你想亲眼看看,请在Silverlight中尝试:

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

如果可以,我会很高兴,因为我可以使用一行来轻松跟踪所有事件进行订阅。但它没有...替代方法是为每个事件编写不同的函数,并将此函数分配给事件。为什么功能不同?好吧,我需要知道哪个事件发布了。如果我对两个不同的事件使用相同的函数,我只将有效负载作为参数。我现在可以找出导致跟踪消息的事件。

我试过了:

  • 使用Reflection获取导致事件(不工作)
  • 在事件中使用构造函数使每个事件能够跟踪自己(不允许)

还有其他想法吗? 克里斯

PS:写这篇文章花了我很可能比为20个事件编写20个函数更长,但我拒绝放弃:-)我只是想使用postharp,这很可能会工作(虽然我不是当然,也许我最终只得到有关基类的信息)..棘手且不那么重要的话题......

2 个答案:

答案 0 :(得分:5)

最简单的事情可能是继承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 :(得分:1)

有点晚,但迟到总比没有好!我最近遇到了同样的问题,这就是我解决它的方法。

首先,我不喜欢发布/订阅事件的Prism方法,所以我使用了这样的方法: http://neverindoubtnet.blogspot.com/2009/07/simplify-prism-event-aggregator.html

上面的这篇文章建议在Event Aggregator上使用Extension方法来简化对发布/订阅的调用。因此,您的客户端代码如下所示:

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>
{
}

好的,这很棒,但是我没有使用hacky扩展方法,而是实现了我自己的事件服务,如下所示:

/// <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使用的实例相同,那么仍然可以工作)。

最后,另一个要添加的技巧是使用Stack Frame告诉我出版物和订阅的来源。请注意,这是一个缓慢的过程(展开堆栈框架),因此请谨慎使用。如果你是 定期发起一个事件然后可能在你的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 app周围的大量事件时,我发现这非常宝贵!