Emesary implementation of C# WPF Application Messaging

I've tried to explain this many ways and it always ends up hard to understand, so this time I'm just trying code.

I've been using this technique since 1996 in various forms and it is always the cornerstone of my architectures. It is simple, extensible and efficient.

This technique will allow decoupled disparate parts of a system to function together, GUI, DB and business logic.

C# .NET Emesary downloads

Base Class Diagram

This is the core of Emesary with one recipient / sender illustrated MainView. All you need to do to get the whole thing to work is simply to implement IReceiver connect to GlobalTransmitter and use GlobalTransmitter.NotifyAll to send messages. That's all there is to it.

Implement the interfaces and construct yourself

To receive messages an object simply needs to implement IReceiver and register itself with the GlobalTransmitter.

    public partial class MenuView  : UserControl, IReceiver
    {
        public MenuView (MenuMatrixEntities ds)
        {
            GlobalTransmitter.Register(this);
        }
  

Connecting buttons

Using good old fashioned callbacks - except all we need to do is to launch and event, and eventually this event will land back at our Receive message in the form of a SelectRecipe message.

        private void addButton_Click(object sender, RoutedEventArgs e)
        {
            GlobalTransmitter.NotifyAll(new Notification(NotificationType.CreateRecipe));
        }

 

Notice how the backend logic is completely abstracted and the flow isn't decided by the UI. The UI is being driven by the logic.

 

Implement the event recipient

This is where all events are received. The object here is to handle anything within the class that is directly related to the class itself. It is permitted, even encouraged, to dispatch further notifications from within this method.

        public ReceiptStatus Receive(Notification message)
        {
            if (message.Type == NotificationType.CreateRecipe)
            {
                Recipe new_item = new Recipe();
                ds.AddToProducts(new_item); 

                /*
                 * now notify that a new recipe has been created an select it
                 */
                GlobalTransmitter.NotifyAll(new Notification(NotificationType.SelectRecipe, new_item));
                return ReceiptStatus.OK;
            }
            else if (message.Type == NotificationType.SelectRecipe)
            {
                if (message.Value is Recipe)
                    recipe = message.Value as Recipe; // the property takes care of the UI controls
            }
            return ReceiptStatus.Fail;
        }

Notifications

The Notification class is simple and designed to be inherited from. A notification is an enumerated type and an object. The type identifies the purpose of each notification and the value can be anything and using RTTI / reflection we can easily extract it.

Sequencing

The order in which objects are created and registered with each transmitter is important, as an individual recipient can provide a canonical response of Abort or Finished - at which point the event will be considered by Emesary to have been processed.

Authorisation

Authorisation is one of those difficult areas to get right. Ideally it needs to be built into everything from the beginning at the lowest level, and yet it still needs to be flexible because we may want to change or enhance the authentication code. Traditional solutions such as a global or singleton restrict this, however by using a notification our class can easily find out if it is permitted to do something.

AuthorisationRequestNotification class

The following is the definition of the Notification that is at the core of the system. It simply takes an Action and an Object Identification and requests that something authorise this action.

    public enum AuthorisationAction
    {
        View,
        Modify,
        Delete,
        Action // ie. command / select / click
    }
    public class AuthorisationRequestNotification : Notification
    {
        public AuthorisationAction action { get; set; }
        public int position { get; set; }

        public AuthorisationRequestNotification(AuthorisationAction action, string objectId)
            : base(NotificationType.IsAuthorisedTo, objectId)
        {
            this.action = action;
        }
    }

Getting authorised

Within any object it becomes a simple matter to verify if an any operation is authorised, simply send a message and check the return. As by default Emesary will return ReceiptStatus.Fail for any unhandled notification this mechanism will by default not authorise unless the object(s) handling the authorisation request allow it.

if (GlobalTransmitter.NotifyAll(new AuthorisationRequestNotification(AuthorisationAction.View, "CostingView"))
    != ReceiptStatus.Fail)
{
    // The action is permitted
}

Inversion of control

There are many ways to achieve IoC; however Emesary solves the problem simply. Taking the lamp and button example we don't need five classes to achieve this, all we need is a Button object a Lamp object and Emesary joining them together.

When I create an object of class Lamp it will automatically work; I can add extra buttons and lamps at will and connect them easily and simply. In the example that follows I've used LampToggle, as to use LampOn and LampOf notifications requires that the button also receives notifications and handles them to allow for more than one button where the button needs to show the current state.

    public partial class Button  : IReceiver
    {
        public Button ()
        {
            GlobalTransmitter.Register(this);
        }
        private void addButton_Click(object sender, RoutedEventArgs e)
        {
            GlobalTransmitter.NotifyAll(new Notification(NotificationType.LampToggle, "Basement-Lamp"));
        }
    }

    
    public partial class Lamp  : IReceiver
    {
        public string lamp_id { get; set; }

        public Lamp (string id)
        {
            GlobalTransmitter.Register(this);
            lamp_id = id;
        }

        public ReceiptStatus Receive(Notification message)
        {
            if (message.Type == NotificationType.LampToggle 
                && messave.Value == lamp_id)
            {
                 // do something to turn the lamp on. 
            }
        }
    }

Class diagram worked example

Other benefits

 

  • Easy and quick logging and inspection of all event flow
  • Access control and permissions within the event flow
  • Two objects can bridge Emesary across process or machine boundaries easily.
  • Ease and simplicity (did I already mention this?)

 

GlobalTransmitter

The GlobalTransmitter is a singleton that provides an easy and quick way to connect an entire application up without really having to worry about where to place the individual Transmitters and how to register everything.

It is possible to use Transmitters on a per-form basis, but there are very few times that this complexity is either required or useful

GlobalTransmitter works well and is simple covering 99% of requirements

There are times when it is necessary to have more than one transmitter, for example

  • When you need a private way of connecting fields on a form for validation and inter-linking of fields within a form, without the need to tell the world

 

Implement the interfaces and construct yourself

To receive messages an object simply needs to implement IReceiver and register itself with the GlobalTransmitter.

Further reading

Emesary design notes