Lightweight distributed application framework with multiple and pluggable backends, providing a set of convenience functions for event handling.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
.gitignore
LICENSE
README.md

README.md

Cambion

Cambion is a lightweight distributed application framework with multiple and pluggable backends, providing a set of convenience functions for event handling. This not only covers regular asynchronous events, but can also call methods on a subscriber synchronously and get data back.

It is an indirect fork of Succubus

Installation

Cambion is available on NuGet so you can install it in the NuGet Package Manager Console:

Install-Package Whitestone.Cambion

Usage

The Basics

Instantiation

var cambion = new CambionMessageHandler();

This instance should preferrably be shared among all usages throughout the code.

Initialization

Then initialize it using the default provided backend:

messageHandler.Initialize(init => { init.UseLoopback(); });

Subscribing

There are two ways to say you want to subscribe to an event or synchronizer. The difference between these is that an event is a one-way fire-and-forget message that is intended for multiple recipients, and a synchronized is a two-way message that is intended for one recipient, but can be called from multiple sources.

Direct subscription

You can subscribe using the methods found in ICambion, which are accessible in your CambionMessageHandler object.

cambion.AddEventHandler<TEvent>(MyEventCallback);
cambion.AddSynchronizedHandler<TRequest, TResponse>(MySynchronizedCallback);

The method signature for the callbacks will then be as follows:

private void MyEventCallback(TEvent data) { }
private TResponse MySynchronizedCallback(TRequest data) { }

You can also use lambda expressions instead of callback methods:

cambion.AddEventHandler<TEvent>(data => { });
cambion.AddSynchronizedHandler<TRequest, TResponse>(data => { return null; });

Make sure that your synchronized methods/expressions always return data.

Interface subscription

Another way to subscribe is using an interfaces. To use this approach, have the class where you want your subscriptions to implement either IEventHandler<> or ISynchronizedHandler<>. You can now register all your subscriptions in a single call to Register().

Example
public class MySubscriptionClass : IEventHandler<TEvent>, ISynchronizedHandler<TRequest, TResponse>
{
    public MySubscriptionClass(ICambion cambion)
    {
        cambion.Register(this);
    }
    
    public void HandleEvent(TEvent data) { }
    
    public TResponse HandleSynchronized(TRequest data)
    {
        return null;
    }
}

Consuming

Once you have all your subscriptions set up you can call the callback methods using the functions provided in ICambion.

Events

Events are fire-and-forget, and there may be multiple recipients.

Publish an event using the following functionality:

cambion.PublishEvent(new TEvent());

All the examples regarding events described above in Subscribing use the TEvent type during subscription, so this call to PublishEvent() will cause the callbacks for all those subscriptions to be called.

Note that you do not need to specify the type as a generic, even though the method signature has it. This will be handled by the compiler.

Synchronized

Synchronized are two-way, and there should be only one recipient.

Publish a synchronized using the following functionality:

TResponse response = cambion.CallSynchronizedHandler<TRequest, TResponse>(new TRequest());

All the examples regarding synchronized described above use the TRequest and TResponse types during subscription, so this call to CallSynchronizedHandler will cause the callbacks for all those subscriptions to be called.

Note that you are here required to specify the types as generics for the method signature.

A note about synchronized messages

Because the distributed backends will use multiple instances of Cambion, a synchronized handler can be set up for the same message signature in multiple instances. Because Cambion sends the synchronized message out to all handlers, there is no way of telling which one will reply, and the reply will be handled by the first instance that is able to send a reply.

Therefore, make sure that no synchronized handler signatures are shared between all your instances.

Backends

A backend is a transport layer to transfer messages. Cambion can easily be configured to use any backend, and you can even create your own, but that's a discussion for another chapter.

Loopback

The default backend that comes preinstalled with Cambion is called Loopback. This does not require any specific configuration, apart from having to tell Cambion to use it.

var cambion = new CambionMessageHandler();
cambion.Initialize(init =>
{
    init.UseLoopback();
}

This backend is limited in that Cambion cannot share data with other applications, or even separate instances of Cambion within the same application. For this you need another backend.

NetMQ

If you have one application that needs to talk to another application, either on the same computer, or even on a different computer, you can't use the loopback backend.

Fortunately Cambion has another backend to cover these use-cases, called NetMQ. This uses NetMQ to send data between instances of Cambion.

NetMQ uses two TCP ports, so you need to have these two ports open in your firewall.

The NetMQ backend for Cambion is also available on NuGet so you can install it using the NuGet Package Manager Console:

Install-Package Whitestone.Cambion.Backend.NetMQ

Clients

NetMQ is setup during regular Cambion initialization. Instead of using the Loopback backend as described above, use the NetMQ backend instead.

var cambion = new CambionMessageHandler();
cambion.Initialize(init =>
{
    init.UseNetMq(
    	publishAddress: "tcp://localhost:9999",
        subscribeAddress: "tcp://localhost:9998"
    );
}

TCP ports 9998 and 9999 are arbitrary ports, and you can use whichever ports you fancy (that are available). Remember that the ports must be the same (both numerical, and the same order) for all instances of Cambion that will talk to eachother.

MessageHost

In order for Cambion to send and receive data using NetMQ, one of the Cambion instances needs to work as a MessageHost. The other Cambion instances will then connect to the MessageHost.

The MessageHost will work as a normal client in addition to being the host without any additional configuration

Initialize the NetMQ backend as normal, but use the optional INetMqConfigurator parameter in addition:

var cambion = new CambionMessageHandler();
cambion.Initialize(init =>
{
    init.UseNetMq(
    	publishAddress: "tcp://localhost:9999",
        subscribeAddress: "tcp://localhost:9998",
        netmq =>
        {
            netmq.StartMessageHost();
        }
    );
}

MEF Compatibility

v1.0.1

Starting from this version, explicit MEF compatibility has been removed. It is, however, still easy to use Cambion with MEF. The following is an example of how to initialize MEF in .NET Core, automatically including Cambion:

ConventionBuilder conventions = new ConventionBuilder();
conventions.ForTypesDerivedFrom<ICambion>()
	.Export<ICambion>()
	.Shared();

ContainerConfiguration containerConfig = new ContainerConfiguration()
	.WithAssembly(Assembly.GetExecutingAssembly(), conventions)
	.WithAssembly(typeof(ICambion).Assembly, conventions);

using (CompositionHost container = containerConfig.CreateContainer())
{
	// Your code here
}

Then in your MEF instantiated classes you can simply import Cambion:

[Import] public ICambion Cambion { get; set; }

v1.0.0

Cambion also supports MEF. You can therefore add an AssemblyCatalog to your catalogues, then MEF will handle the instantiation and sharing throughout the code:

var cambionAssemblyCatalog = new AssemblyCatalog(typeof(ICambion).Assembly)

Then you can have MEF import it automatically based on convention:

[Import]
private ICambion _cambion;

Or you can use the MEF container to get an instance:

var cambion = yourCompositionContainer.GetExportedValue<ICambion>();