Hide menu Last updated: Jul 09 2014

This document describes the event management system API in EPiServer, you can use this system to send events in a load balanced environment.

Event classes and methods

The EPiServer.Events.Clients.Event class is the public API of the EPiServer event system. It allows for the sending and receiving of site events in a de-coupled manner. The event system does not have any predefined events when EPiServer CMS starts. It is up to the code that wants to send and receive events to register them using the Event.GetEvent static method. The GetEvent method accepts a GUID event ID, and will create a new event object instance if one has not already been created for the event ID. Otherwise it will return the existing one. It is important to note that the GUID event ID is only important to users of the event system in that it allows different pieces of code to identify the same event. The event system has no knowledge of what these events are or who uses them.

The Event class has the following .NET events that user code can subscribe to:

  • The Raised event is fired when some code has called the Raise method of the Event class.
  • The Missed is fired when the event system detects that a remote event (an event received from another site or server) has gone missing. The algorithm for detecting a missing event is based upon tracking the sequence numbers allocated to remote events and detecting after a predetermind amount of time that one or more are missing.

Example of raising and receiving events

The following example shows how to set up an event handler for a custom event, including a method for raising a custom event:

C#
//Generate unique id for your event and the raiser
private static readonly Guid RaiserId = new Guid("F080ABD6-9BB0-4DC0-9D95-265472FA1CC6");
private static readonly Guid EventId = new Guid("888B5C89-9B0F-4E67-A3B0-6E660AB9A60F");

public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context)
{
    //Set up event handler
    Event myEvent = Event.Get(EventId);
    myEvent.Raised += myEvent_Raised;
}

void myEvent_Raised(object sender, EventNotificationEventArgs e)
{
     // don't process events locally raised
    if (e.RaiserId != RaiserId)
    {
        //Do something, e.g. invalidate cache
    }
}

public void RaiseEvent(string message)
{
    Event.Get(EventId).Raise(RaiserId, message);
}

Configuring an event provider

For the built-in WCF-based event provider it is enough to define WCF end-points as described in the section Configuring events over WCF. To use another event provider, add the provider configuration to the event element in the episerver.framework section of the web.config as the following example shows:

        <event defaultProvider="msmq">
        <providers>
            <add name="msmq" type="MSMQEventProvider.MSMQEventProvider, MSMQEventProvider" />
        </providers>
        </event>

You can find more providers, such as the Amazon provider and the Azure provider on EPiServer NuGet.

Implementing an event provider

The following example shows a simple event provider that uses MSMQ for message transportation between IIS sites on same machine:

C#
using EPiServer.Events;
using EPiServer.Events.Providers;
using EPiServer.Framework.Configuration;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
using EPiServer.Web;
using EPiServer.Web.Hosting;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MSMQEventProvider
{
    #region msmq
    public class MSMQEventProvider : EventProvider
    {
        private const string EPiServerQueueName = "private$\\episerverevents";
        MessageQueue _sendQueue;
        private static object _sendLock = new object();
        IList<Timer> _receiveTimers = new List<Timer>();
        TimeSpan _waitTimeout = new TimeSpan(0, 0, 0, 0, 10);

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

            //Get all existing EPiServer queues
            var queues = MessageQueue.GetPrivateQueuesByMachine(Environment.MachineName)
                .Where(q => q.QueueName.StartsWith(EPiServerQueueName));
            _sendQueue = queues.SingleOrDefault(q => q.QueueName.EndsWith(CurrentApplicationId));

            if (_sendQueue == null)
            {
                string queuePath = ".\\" + EPiServerQueueName + CurrentApplicationId;
                _sendQueue = MessageQueue.Create(queuePath);
                _sendQueue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
                _sendQueue.Formatter = new BinaryMessageFormatter();
            }

            //Set up recieve listners for all other queues
            foreach (var queue in queues.Where(q => !q.QueueName.EndsWith(CurrentApplicationId)))
            {
                queue.Formatter = new BinaryMessageFormatter();
                _receiveTimers.Add(new Timer(new TimerCallback(Receive), queue, 0, 1000));
            }
        }

        public override void Uninitialize()
        {
            base.Uninitialize();
            foreach (var timer in _receiveTimers)
            {
                timer.Dispose();
            }
            _sendQueue.Dispose();
        }

        public override void SendMessage(EventMessage message)
        {
            lock (_sendLock)
            {
                _sendQueue.Send(message);
            }
        }

        private void Receive(object state)
        {
            MessageQueue queue = state as MessageQueue;
            Message message = null;
            //Receive throws for empty queue therefore the check with enumerator
            while (!IsQueueEmpty(queue))
            {
                message = queue.Receive(_waitTimeout);
                OnMessageReceived(new EventMessageEventArgs(message.Body as EventMessage));
            }
        }

        private bool IsQueueEmpty(MessageQueue queue)
        {
            using (MessageEnumerator enumerator = queue.GetMessageEnumerator2())
            {
                return !enumerator.MoveNext();
            }
        }

        private string CurrentApplicationId
        {
            get { return GenericHostingEnvironment.Instance.ApplicationID.Replace('/', '_').ToLower(); }
        }

    }
    #endregion
}

See also

Comments