The PostSharp (AOP) with The Pub/Sub Events Aspects

Today I would like to share a few examples of PostSharp aspects combined with Pub/Sub Event System. That example was created for my Workshop Training, and I would like to share this example with you. I build WeChat. As you probably suppose, every instance of a Wsp Chat can communicate with each other. Let’s start with the beginning of the creation of this project. First, what I created was an empty WspChat class and then a WspEventChatForm class. It looks like it is shown below.

namespace PostSharpTrainingWorkshopExemples.Chat.Business
{
  using System;
  internal class WspChat : IDisposable
  {
    public MsgEvent Send(string message)
    {
      return new MsgEvent{Message = message};
    }
    public delegate void MessageReceivedEventHandler(MsgEvent @event);
    public event MessageReceivedEventHandler Received;
    public void Dispose() { }
  }
}
namespace PostSharpTrainingWorkshopExemples.Chat.Forms
{
  using System;
  using System.Diagnostics;
  using System.Windows.Forms;
  using PostSharpTrainingWorkshopExemples.Chat.Business;
  public partial class WspEventChatForm : Form
  {
    readonly WspChat m_chat = new WspChat();
    public WspEventChatForm()
    {
      InitializeComponent();
    }
    private void WspEventChatFormLoad(object sender, System.EventArgs e)
    {
      m_chat.Received += new WspChat.MessageReceivedEventHandler(ChatMessageReceived);
    }
    void ChatMessageReceived(MsgEvent @event)
    {
      Invoke(new Action(() =>
         {
           m_lbxChat.Items.Add(@event.Message);
           for (var i = m_lbxChat.Items.Count - 1; i > 100; i--)
             m_lbxChat.Items.RemoveAt(0);
           m_lbxChat.SelectedIndex = m_lbxChat.Items.Count - 1;
         }
       ));
    }
    private void BtSendClick(object sender, System.EventArgs e)
    {
      if (string.IsNullOrEmpty(m_tbMessage.Text))
        return;
      var message = string.Format("{0}: {1}", Environment.UserName, m_tbMessage.Text);
      m_tbMessage.Text = string.Empty;
      m_chat.Send(message);
    }
    private void WspEventChatFormFormClosed(object sender, FormClosedEventArgs e)
    {
      m_chat.Dispose();
      Environment.Exit(0);
    }
  }
}

Of course I need also MsgEvent and here is an example of it.

namespace PostSharpTrainingWorkshopExemples.Chat.Business
{
  using System;
  using Microsoft.WebSolutionsPlatform.Event;
  public class MsgEvent : Event
  {
    readonly Guid EVENT_TYPE = new Guid(@"E42AD74A-2A09-427C-9F5B-6899D610D637");
    public string Message { get; set; }
    public MsgEvent()
      : base()
    {
      EventType = EVENT_TYPE;
    }
    public MsgEvent(byte[] serializationData)
      : base(serializationData)
    {
      EventType = EVENT_TYPE;
    }
    public override void GetObjectData(WspBuffer buffer)
    {
      buffer.AddElement(@"Message", Message);
    }
  }
}

And now it is most difficult time. I need to prepare the Communication component. I created a singleton pattern class with three public operations Receive for a callback when events come, Request for send events and Release for disposing resources.

namespace PostSharpTrainingWorkshopExemples.Communication.Share
{
  using System;
  using System.Collections.Concurrent;
  using Microsoft.WebSolutionsPlatform.Event;
  using Microsoft.WebSolutionsPlatform.Event.PubSubManager;
  public class Component
  {
    static Component m_instance;
    static readonly object m_locker = new object();
    internal static Component Instance
    {
      get
      {
        lock (m_locker)
          m_instance = m_instance ?? new Component();
        return m_instance;
      }
    }
    PublishManager m_publisher;
    SubscriptionManager.Callback m_callback;
    SubscriptionManager m_subscriber;
    ConcurrentDictionary<Guid, Action<byte[]>> m_events;
    private Component()
    {
      m_publisher = new PublishManager();
      m_callback = new SubscriptionManager.Callback(EventCallback);
      m_subscriber = new SubscriptionManager(m_callback);
      m_events = new ConcurrentDictionary<Guid, Action<byte[]>>();
    }
    void EventCallback(Guid eventType, byte[] serializationData)
    {
      Action<byte[]> action;
      if (m_events.TryGetValue(eventType, out action))
      {
        action(serializationData);
      }
    }
    public void Receive(Event @event, Action<byte[]> action)
    {
      if (m_events.TryAdd(@event.EventType, action))
        m_subscriber.AddSubscription(@event.EventType, true);
    }
    public void Request(Event @event)
    {
      m_publisher.Publish(@event.Serialize());
    }
    public void Release()
    {
      foreach (var @event in m_events)
      {
        m_subscriber.RemoveSubscription(@event.Key);
        Action<byte[]> action;
        m_events.TryRemove(@event.Key, out action);
      }
      lock (m_locker)
      {
        if (m_events == null)
        {
          m_events.Clear();
          m_events = null;
        }
        if (m_subscriber != null)
        {
          m_subscriber.ListenForEvents = false;
          m_subscriber.Dispose();
          m_subscriber = null;
        }
        if (m_callback != null)
        {
          m_callback = null;
        }
        if (m_publisher != null)
        {
          m_publisher.Dispose();
          m_publisher = null;
        }
      }
    }
  }
}

An now it is time to create aspects for my comunication compoent.

namespace PostSharpTrainingWorkshopExemples.Communication
{
  using System;
  using Microsoft.WebSolutionsPlatform.Event;
  using PostSharp.Aspects;
  using PostSharpTrainingWorkshopExemples.Common;
  using PostSharpTrainingWorkshopExemples.Communication.Share;
  [Serializable]
  public class WspReceiverAttribute : EventInterceptionAspect
  {
    [NonSerialized]
    Event @event = null;
    [NonSerialized]
    Arguments arguments;
    public override void OnAddHandler(EventInterceptionArgs args)
    {
      args.ProceedAddHandler();
      if (@event == null)
        @event = (Event)Activator.CreateInstance(
          args.Handler.Method.GetParameters()[0].ParameterType);
      if (arguments == null)
        arguments = PostSharpHelpers.CreateArguments(@event.GetType());
      base.OnAddHandler(args);
      Action<byte[]> action =
      serializedData =>
      {
        @event.Deserialize(serializedData);
        arguments.SetArgument(0, @event);
        args.InvokeHandler(args.Handler, arguments);
      };
      Component.Instance.Receive(@event, action);
    }
  }
}

Aspects above is most tricky in whole application. But also, it is the most fun part of it ;). The last two aspects look like it is shown below.

namespace PostSharpTrainingWorkshopExemples.Communication
{
  using System;
  using Microsoft.WebSolutionsPlatform.Event;
  using PostSharp.Aspects;
  using PostSharpTrainingWorkshopExemples.Communication.Share;
  [Serializable]
  public class WspRequesterAttribute : OnMethodBoundaryAspect
  {
    public override void OnSuccess(MethodExecutionArgs args)
    {
      base.OnSuccess(args);
      args.FlowBehavior = FlowBehavior.Return;
      var @event = args.ReturnValue as Event;
      Component.Instance.Request(@event);
    }
  }
}
namespace PostSharpTrainingWorkshopExemples.Communication
{
  using System;
  using PostSharp.Aspects;
  using PostSharpTrainingWorkshopExemples.Communication.Share;
  [Serializable]
  public class WspDisposeAttribute : OnMethodBoundaryAspect
  {
    public override void OnSuccess(MethodExecutionArgs args)
    {
      base.OnSuccess(args);
      args.FlowBehavior = FlowBehavior.Return;
      Component.Instance.Release();
    }
  }
}

Ok, so we have communication component and aspects. Lets modify our WspChat class.

namespace PostSharpTrainingWorkshopExemples.Chat.Business
{
  using System;
  using PostSharpTrainingWorkshopExemples.Communication;
  internal class WspChat : IDisposable
  {
    [WspRequester]
    public MsgEvent Send(string message)
    {
      return new MsgEvent{Message = message};
    }
    public delegate void MessageReceivedEventHandler(MsgEvent @event);
    [WspReceiver]
    public event MessageReceivedEventHandler Received;
    [WspDispose]
    public void Dispose() { }
  }
}

Three attributes and that’s it. WeChat works now. We simply create aspects and put them into business logic. If you want to play with this example you need to have Professional Edition of PostSharp 2.0 (because of EventInterceptionAspect, but remember that on every Monday Community Edition turns into Professional Edition).

You can also concern three task subjects:

1. How to change the project to use only community edition aspects?

2. Write chat boot that can answer commands like “/time” to answer the current date time or, for example, “/bing [subject to find]” that gives you the most popular link in bing with [subject to find].

3. You can also configure Pub/Sub Events to work in the local network for chatting with others.

And also at last something to thinking: What is one line implementation of invoked method: PostSharpHelpers.CreateArguments(@event.GetType());

I hope you enjoy those examples of aspects.

Best regards,

P ;).

2 Replies to “The PostSharp (AOP) with The Pub/Sub Events Aspects”

  1. Thanks for the comment, Britt. I had Professional Edition of the PostSharp Framework since the time of presentation about PostSharp in Cracow presented by Gael Fraiteur. I think that Aspect Oriented Programming completly changes my experiences in .NET technology. I started my PostSharp adventure with thought about different programing matters. Thanks to the SharpCrafters Team for grat AOP Framwework .

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.