Hide menu Last updated: Sep 21 2015

This topic describes the Episerver event management system API. You can use this system to send events in a load-balanced environment.

Event classes and methods

The EPiServer.Events.Clients.IEventRegistry interface is the public API of the Episerver event system that lets you send and receive 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 IEventRegistry.Get method. The Get method accepts a GUID event ID, and creates a new event object instance if one was not already created for the event ID. Else, it returns 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 lets different pieces of code identify the same event. The event system has no knowledge of what these events are or who uses them.

The Event class returned from the Get method 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
    var myEvent = EventRegistry.Instance.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)
{
    EventRegistry.Instance.Get(EventId).Raise(RaiserId, message);
}

Using custom event arguments

In the previous example, a string is sent as event argument, the data for the event. You also can send custom objects as event arguments, as long as you can serialize both using data contract serialization(WCF) and JSON serialization(cloud).

Event arguments should be small and simple classes. For example the maximum message size in Azure is 256 Kb (but due to system overhead, this limit is usually slightly less than 256 Kb). Sending large amounts of data in event arguments does not scale.

Example event argument:

[DataContract]
[EventsServiceKnownType]
public class MyEventData
{
    [DataMember]
    public string Message { get; set; }
    [DataMember]
    public int MyNumber { get; set; }
}

 

Configuring an event provider

For the built-in WCF-based event provider it is enough to define WCF end-points as described in 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 a custom event provider

The following example shows a simple event provider that uses MSMQ for message transportation between IIS sites on same machine. This code is not ready for production use, it is only provided as an example of implementing an event provider for an external system.

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
}

Related topics

Comments