How to Dispatch Events in C#

How to dispatch events in C#

There is a pattern that is used in all library classes. It is recommended for your own classes too, especially for framework/library code. But nobody will stop you when you deviate or skip a few steps.

Here is a schematic based on the simplest event-delegate, System.Eventhandler.

// The delegate type. This one is already defined in the library, in the System namespace
// the `void (object, EventArgs)` signature is also the recommended pattern
public delegate void Eventhandler(object sender, Eventargs args);

// your publishing class
class Foo
{
public event EventHandler Changed; // the Event

protected virtual void OnChanged() // the Trigger method, called to raise the event
{
// make a copy to be more thread-safe
EventHandler handler = Changed;

if (handler != null)
{
// invoke the subscribed event-handler(s)
handler(this, EventArgs.Empty);
}
}

// an example of raising the event
void SomeMethod()
{
if (...) // on some condition
OnChanged(); // raise the event
}
}

And how to use it:

// your subscribing class
class Bar
{
public Bar()
{
Foo f = new Foo();
f.Changed += Foo_Changed; // Subscribe, using the short notation
}

// the handler must conform to the signature
void Foo_Changed(object sender, EventArgs args) // the Handler (reacts)
{
// the things Bar has to do when Foo changes
}
}

And when you have information to pass along:

class MyEventArgs : EventArgs    // guideline: derive from EventArgs
{
public string Info { get; set; }
}

class Foo
{
public event EventHandler<MyEventArgs> Changed; // the Event
...
protected virtual void OnChanged(string info) // the Trigger
{
EventHandler handler = Changed; // make a copy to be more thread-safe
if (handler != null)
{
var args = new MyEventArgs(){Info = info}; // this part will vary
handler(this, args);
}
}
}

class Bar
{
void Foo_Changed(object sender, MyEventArgs args) // the Handler
{
string s = args.Info;
...
}
}

Update

Starting with C# 6 the calling code in the 'Trigger' method has become a lot easier, the null test can be shortened with the null-conditional operator ?. without making a copy while keeping thread-safety:

protected virtual void OnChanged(string info)   // the Trigger
{
var args = new MyEventArgs{Info = info}; // this part will vary
Changed?.Invoke(this, args);
}

Delaying the creation and dispatch of domain events

One solution (as suggested by @synhershko) is to move the dispatching of the domain events outside of the domain. This way we can ensure our entity is persisted before we raise any events.

However, we're now moving behaviour out of the domain (where it belongs) into our application just to work around our persistence technology - which I'm not that happy about.

My solution to Events should only be dispatched if the entity is successfully persisted was to create a deferred event dispatcher that queues the events. We then inject the dispatcher into our unit of work ensuring that we first persist/save our entity and then emit the domain events:

public class DeferredEventDispatcher : IEventDispatcher
{
private readonly IEventDispatcher inner;
private readonly ConcurrentQueue<Action> events = new ConcurrentQueue<Action>();

public DeferredEventDispatcher(IEventDispatcher inner)
{
this.inner = inner;
}

public void Dispatch<TEvent>(TEvent e)
{
events.Enqueue(() => inner.Dispatch(e));
}

public void Resolve()
{
Action dispatch;
while (events.TryDequeue(out dispatch))
{
dispatch();
}
}
}

public class UnitOfWork
{
public void Commit()
{
session.SaveChanges();
dispatcher.Resolve(); // raise events
}
}

Essentially this achieves the same thing as suggested by @synhershko but keeps the "raising" of events within my domain.

As for Events should not be created until the entity is persisted the main issue was that entity identifiers were being set externally by RavenDB. A solution that keeps my domain persistent ignorant and easy to test is to simply pass the id as a constructor parameter. This is what I would have done if using a SQL database (usually passing a Guid).

Fortunately RavenDB does provide a way for you to generate identifiers using the hilo strategy (so we can keep RESTful identifiers). This is from the RavenDB Contrib project:

public static string GenerateIdFor<T>(this IAdvancedDocumentSessionOperations session)
{
// An entity instance is required to generate a key, but we only have a type.
// We might not have a public constructor, so we must use reflection.
var entity = Activator.CreateInstance(typeof(T), true);

// Generate an ID using the commands and conventions from the current session
var conventions = session.DocumentStore.Conventions;
var databaseName = session.GetDatabaseName();
var databaseCommands = session.GetDatabaseCommands();
return conventions.GenerateDocumentKey(databaseName, databaseCommands, entity);
}

I can then use this to generate an ID and pass it in my entity constructors:

var orderId = session.GenerateIdFor<Order>();
var order = new Order(orderId, 1.99M);

If I register for an event in c# while it's dispatching, am I guaranteed to not get called again during that dispatch?

Yes, it's guaranteed.

From the unified C# 3.0 spec, section 15.1:

However, when two non-null delegate
instances are combined, their
invocation lists are concatenated—in
the order left operand then right
operand—to form a new invocation list,
which contains two or more entries.

Note the "new invocation list". And again in section 15.3:

Once instantiated, delegate instances
always refer to the same target object
and method. Remember, when two
delegates are combined, or one is
removed from another, a new delegate
results with its own invocation list;
the invocation lists of the delegates
combined or removed remain unchanged.

Finally, MSDN for System.Delegate states:

Delegates are immutable; once created,
the invocation list of a delegate does
not change.

I suspect there's something in the CLI spec - I'll check if you'd like, but hopefully these three have given you enough confidence :)

Implementing Domain Event Handler pattern in C# with Simple Injector

The solution you need is a bit dependent on how the consumer of the Dispatcher calls events. If the consumer always knows the exact type of the event at compile time, you can use the generic Dispatch<TEvent>(TEvent) method as you show above. In that case the Dispatcher's implementation will be really straightforward.

If on the other hand, consumers might not always know the exact type, but simply work with the IEvent interface, the generic type argument in Dispatch<TEvent>(TEvent) becomes useless, and you would be better off defining a Dispatch(IEvent) method. This makes the implementation a bit more sophisticated, because you will need to use reflection to solve this.

Also note that it would be good to introduce an IEventDispatcher abstraction. Don't call a static class from within your code. Even Udi Dahan (who initially described such static class a long time ago) now considers this an anti-pattern. Instead, inject an IEventDispatcher abstraction into classes that require event dispatching.

In case all consumers work with event types that are known at compile time, your implementation will look as follows:

public interface IEventDispatcher
{
void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent;
}

private sealed class Dispatcher : IEventDispatcher
{
private readonly Container container;
public Dispatcher(Container container) {
this.container = container;
}

public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent {
if (@event == null) throw new ArgumentNullException("event");

var handlers = this.container.GetAllInstances<IEventHandler<TEvent>>();

foreach (var handler in handlers) {
handler.Handle(@event);
}
}
}

If on the other hand, event types are unknown, you can use the following code:

public interface IEventDispatcher
{
void Dispatch(IEvent @event);
}

private sealed class Dispatcher : IEventDispatcher
{
private readonly Container container;
public Dispatcher(Container container) {
this.container = container;
}

public void Dispatch(IEvent @event) {
if (@event == null) throw new ArgumentNullException("event");

Type handlerType = typeof(IEventHandler<>).MakeGenericType(@event.GetType());

var handlers = this.container.GetAllInstances(handlerType);

foreach (dynamic handler in handlers) {
handler.Handle((dynamic)@event);
}
}
}

Do note that the use of the dynamic keyword has a few unobvious advantages over using the .NET reflection API. For instance, when calling the handler's Handle method using dynamic, any exception thrown from the handle will bubble up directly. When using MethodInfo.Invoke on the other hand, the exception will be wrapped in a new exception. This makes catching and debugging harder.

Your event handlers can be registered as follows:

container.Collection.Register(typeof(IEventHandler<>), listOfAssembliesToSearch);

How to correctly fire an event in C#

I solved my problem in this way:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace eventsC
{

public partial class Form1 : Form
{

public static event EventHandler<EventArgs> myEvent;

protected void OnMyEvent()
{
if (myEvent != null)
myEvent(this, EventArgs.Empty);
}

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
myEvent += Handler;

//call all methods that have been added to the event
myEvent(this, EventArgs.Empty);

}

private void button1_Click(object sender, EventArgs e)
{
OnMyEvent();
}

static void Handler(object sender, EventArgs args)
{
Console.WriteLine("Event Handled!");
}

}
}

Calling an event handler in C#

It invokes all registered event listeners which are registered on the ThresholdReached event.

The handler != null check makes sure at least one listener is registered to that event.

In C# 6.0 and above you can use Null Propagation:

handler?.Invoke(this, e);

handler(this, e) will call every registered event listener. Event listeners subscribe with help of the += operator and unsubscribe with -= operator to that event.

this is there to give the event listener to know who raised the ThresholdReached event. Who was the sender of the event.

e is the event argument which is also passed into the listener method which can contain more useful informations about the ThresholdReached event e.g. which threshold was reached.

simple custom event

This is an easy way to create custom events and raise them. You create a delegate and an event in the class you are throwing from. Then subscribe to the event from another part of your code. You have already got a custom event argument class so you can build on that to make other event argument classes. N.B: I have not compiled this code.

public partial class Form1 : Form
{
private TestClass _testClass;
public Form1()
{
InitializeComponent();
_testClass = new TestClass();
_testClass.OnUpdateStatus += new TestClass.StatusUpdateHandler(UpdateStatus);
}

private void UpdateStatus(object sender, ProgressEventArgs e)
{
SetStatus(e.Status);
}

private void SetStatus(string status)
{
label1.Text = status;
}

private void button1_Click_1(object sender, EventArgs e)
{
TestClass.Func();
}

}

public class TestClass
{
public delegate void StatusUpdateHandler(object sender, ProgressEventArgs e);
public event StatusUpdateHandler OnUpdateStatus;

public static void Func()
{
//time consuming code
UpdateStatus(status);
// time consuming code
UpdateStatus(status);
}

private void UpdateStatus(string status)
{
// Make sure someone is listening to event
if (OnUpdateStatus == null) return;

ProgressEventArgs args = new ProgressEventArgs(status);
OnUpdateStatus(this, args);
}
}

public class ProgressEventArgs : EventArgs
{
public string Status { get; private set; }

public ProgressEventArgs(string status)
{
Status = status;
}
}

How to 'await' raising an EventHandler event

Events don't mesh perfectly with async and await, as you've discovered.

The way UIs handle async events is different than what you're trying to do. The UI provides a SynchronizationContext to its async events, enabling them to resume on the UI thread. It does not ever "await" them.

Best Solution (IMO)

I think the best option is to build your own async-friendly pub/sub system, using AsyncCountdownEvent to know when all handlers have completed.

Lesser Solution #1

async void methods do notify their SynchronizationContext when they start and finish (by incrementing/decrementing the count of asynchronous operations). All UI SynchronizationContexts ignore these notifications, but you could build a wrapper that tracks it and returns when the count is zero.

Here's an example, using AsyncContext from my AsyncEx library:

SearchCommand = new RelayCommand(() => {
IsSearching = true;
if (SearchRequest != null)
{
AsyncContext.Run(() => SearchRequest(this, EventArgs.Empty));
}
IsSearching = false;
});

However, in this example the UI thread is not pumping messages while it's in Run.

Lesser Solution #2

You could also make your own SynchronizationContext based on a nested Dispatcher frame that pops itself when the count of asynchronous operations reaches zero. However, you then introduce re-entrancy problems; DoEvents was left out of WPF on purpose.



Related Topics



Leave a reply



Submit