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 WspChat. As you probably suppose every instance of a Wsp Chat can communicate with each other. Lets start from the beginning of creation of this project. First what I was created was a 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 Communication component. I create singleton pattern class with three public operations Receive for callback when events came, 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 whoe application. But also it is most fun part of it ;). Last two aspects looks 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 thats it. WspChat 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 (becouse 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 project to use only community edition aspects?

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

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

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

I hope you enjoy those examples of aspects.

Best regards,

P ;).

0 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.