Mirolim.EventBus.RabbitMQ 1.5.2

dotnet add package Mirolim.EventBus.RabbitMQ --version 1.5.2                
NuGet\Install-Package Mirolim.EventBus.RabbitMQ -Version 1.5.2                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mirolim.EventBus.RabbitMQ" Version="1.5.2" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Mirolim.EventBus.RabbitMQ --version 1.5.2                
#r "nuget: Mirolim.EventBus.RabbitMQ, 1.5.2"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Mirolim.EventBus.RabbitMQ as a Cake Addin
#addin nuget:?package=Mirolim.EventBus.RabbitMQ&version=1.5.2

// Install Mirolim.EventBus.RabbitMQ as a Cake Tool
#tool nuget:?package=Mirolim.EventBus.RabbitMQ&version=1.5.2                

There are two libraries

1. EventBus.RabbitMQ

EventBus.RabbitMQ is a messaging library designed to simplify the implementation of communication using RabbitMQ. It enables seamless publishing and receiving of events between microservices or other types of applications. The library is easy to set up and is compatible with .NET8 or recent frameworks. Additionally, it supports working with multiple virtual hosts in RabbitMQ.

With this library, you can easily implement the Inbox and outbox patterns in your application. It allows you to persist all incoming and outgoing event messages in the database. Currently, it supports storing event data only in a PostgreSQL database.

NuGet package

Version Downloads

See the EventBus.RabbitMQ documentation for more information

2. EventStorage

EventStorage is a library designed to simplify the implementation of the Inbox and outbox patterns for handling multiple types of events in your application. It allows you to persist all incoming and outgoing event messages in the database. Currently, it supports storing event data only in a PostgreSQL database.

NuGet package

Version Downloads

See the EventStorage documentation for more information

Getting started the EventBus.RabbitMQ

EventBus.RabbitMQ is a messaging library designed to simplify the implementation of communication using RabbitMQ. It enables seamless publishing and receiving of events between microservices or other types of applications. The library is easy to set up and is compatible with .NET8 or recent frameworks. Additionally, it supports working with multiple virtual hosts in RabbitMQ.

With this library, you can easily implement the Inbox and outbox patterns in your application. It allows you to persist all incoming and outgoing event messages in the database. Currently, it supports storing event data only in a PostgreSQL database.

Setting up the library

Make sure you have installed and run RabbitMQ in your machine. After that, you need to install Mirolim.EventBus.RabbitMQ NuGet package.

Install-Package Mirolim.EventBus.RabbitMQ

Register the nuget package's necessary services to the services of DI in the Program.cs and pass the assemblies to find and load the publishers and subscribers automatically:

builder.Services.AddRabbitMQEventBus(builder.Configuration, assemblies: [typeof(Program).Assembly]);

Create and publish an event massage

Start creating an event to publish. Your record must implement the IPublishEvent interface or inherit from the PublishEvent record. Example:

public record UserDeleted : PublishEvent
{
    public Guid UserId { get; set; }
    
    public string UserName { get; set; }
}

To publish your event, you must first inject the IEventPublisherManager interface from the DI and pass your event object to the Publish method. Then, your event will be published.

public class UserController : ControllerBase
{
    private readonly IEventPublisherManager _eventPublisherManager;

    public UserController(IEventPublisherManager eventPublisherManager)
    {
        _eventPublisherManager = eventPublisherManager;
    }
    
    [HttpPost]
    public IActionResult Create([FromBody] User item)
    {
        Items.Add(item.Id, item);

        var userCreated = new UserCreated { UserId = item.Id, UserName = item.Name };
        _eventPublisherManager.Publish(userCreated);
        
        return Ok(item);
    }
}

Create a subscriber to the event

If you want to subscribe to necessary an event, first you need to create your own an event structure to subscribe. Your subscribe record must implement the ISubscribeEvent interface or inherit from the SubscribeEvent record. Example:

public record UserCreated : SubscribeEvent
{
    public Guid UserId { get; set; }

    public string UserName { get; set; }
}

Then you need to create an event subscriber to receive an event. Your event subscriber class must implement the IEventSubscriber<> interface and implement your subscriber event structure. Example:

public class UserCreatedSubscriber : IEventSubscriber<UserCreated>
{
    private readonly ILogger<UserCreatedSubscriber> _logger;

    public UserCreatedSubscriber(ILogger<UserCreatedSubscriber> logger)
    {
        _logger = logger;
    }

    public async Task<bool> Receive(UserCreated @event)
    {
        _logger.LogInformation("EventId ({EventId}): '{UserName}' user is created with the {UserId} id", @event.EventId,
            @event.UserName, @event.UserId);

        return await Task.FromResult(true);
    }
}

Depend on your business logic, you need to add your logic to the Receive method of subscriber to do something based on your received event.

Advanced configuration of publishers and subscribers from configuration file.

First you need to add a new section called RabbitMQSettings to your configuration file.

"RabbitMQSettings": {
    "DefaultSettings": {
      "IsEnabled": true,
      "HostName": "localhost",
      "HostPort": 5672,
      "VirtualHost": "users/pro",
      "UserName": "admin",
      "Password": "admin123",
      "ExchangeName": "users_exchange",
      "ExchangeType": "topic",
      "QueueName": "users_queue",
      "RoutingKey": "users.created",
      "RetryConnectionCount": 5,
      "QueueArguments": {
        "x-queue-type": "quorum"
      }
    },
    "Publishers": {
      "UserDeleted": {
        "VirtualHost": "users/test",
        "RoutingKey": "users.deleted",
        "PropertyNamingPolicy": "KebabCaseLower"
      },
      "UserUpdated": {
        "RoutingKey": "users.updated",
        "EventTypeName": "UserUpdatedEvent"
      }
    },
    "Subscribers": {
      "PaymentCreated": {
        "ExchangeName": "payments_exchange",
        "VirtualHost": "users/test",
        "RoutingKey": "payments.created",
        "QueueArguments": {
          "x-queue-type": "classic",
          "max-length-bytes": 1048576
        }
      }
    }
  },

A section may have the following subsections: <br/> DefaultSettings - to set the default configuration/settings for connecting to the RabbitMQ and publishing and receiving messages. If you don't pass them, it will use default settings of RabbitMQ; The default settings has optional parameter named QueueArguments to pass the arguments to the queue. Another thing is that, by passing false to the IsEnabled option, we able to just disable using RabbitMQ.<br/> Publishers - set custom settings for the publishers if needed. If you don't pass them, it will use the default settings configured in the DefaultSettings section or RabbitMQ's default settings; <br/> Subscribers - set custom settings for the subscribers if needed. If you don't pass them, it will use the default settings configured in the DefaultSettings section or RabbitMQ's default settings. The subscriber event has optional parameter named QueueArguments to pass the arguments to the queue.

Customizing the event type of publishing/subscribing event:

While publishing or subscribing an event by default it uses the Name of event structure. For example, if you add an event named UserUpdated, while publishing or subscribing/receiving that UserUpdated name as event type will be used. But if you want you can overwrite the event type by added event type name to the config file:

"RabbitMQSettings": {
    "DefaultSettings": {
      //your settings
    },
    "Publishers": {
      "UserUpdated": {
        "RoutingKey": "users.updated",
        "EventTypeName": "UserUpdatedEvent"
      }
    },
    "Subscribers": {
      "UserDeleted": {
        //your settings
        "EventTypeName": "MyUserDeletedEvent"
      }
    }
  }
What if I want to subscribe to an event from another system that doesn't publish an event type?

When RabbitMQ receives an event from a 'Consumer', it tries to read the event type from the received event, if it can't find it, it uses the 'routing key' instead to find the event subscriber.

Advanced configuration of publishers and subscribers while registering to the DI services.

Since the library is designed to work with multiple a virtual hosts of RabbitMQ, there is a way to configure each publisher and subscriber separately from the configuration file or while registering to the DI services.

builder.Services.AddRabbitMQEventBus(builder.Configuration,
    assemblies: [typeof(Program).Assembly],
    defaultOptions: options =>
    {
        options.HostName = "localhost";
    },
    eventPublisherManagerOptions: publisherManager =>
    {
        publisherManager.AddPublisher<UserDeleted>(op => op.RoutingKey = "users.deleted");
        publisherManager.AddPublisher<UserUpdated>(op => op.RoutingKey = "users.updated");
    },
    eventSubscriberManagerOptions: subscriberManager =>
    {
        subscriberManager.AddSubscriber<PaymentCreated, PaymentCreatedHandler>(op =>
        {
            op.VirtualHost = "users/test";
        });
    }
);

defaultOptions - it is an alternative way of overwriting DefaultSettings settings, to set the default configuration/settings for connecting to the RabbitMQ and publishing and receiving messages. If you don't pass them, it will use default settings of RabbitMQ; <br/> eventPublisherManagerOptions - it is an alternative way of overwriting Publishers settings, to register and set custom settings for the publishers if needed. If you don't pass them, it will use the default settings configured in the DefaultSettings section or RabbitMQ's default settings; <br/> eventSubscriberManagerOptions - it is an alternative way of overwriting Subscribers settings, to register and set custom settings for the subscribers if needed. If you don't pass them, it will use the default settings configured in the DefaultSettings section or RabbitMQ's default settings; <br/> assemblies - as I mentioned in above, it is to find and load the publishers and subscribers and register them to the services of DI automatically. It can be multiple assemblies depend on your design.

Adding property to the publishing event's headers

Before publishing an event, you can attach properties to the event's headers by passing the header name and value to the AddHeader method. Keep in mind, the header name must be unique, otherwise it will throw exception. Example:

var userUpdated = new UserUpdated { UserId = item.Id, OldUserName = item.Name, NewUserName = newName };
userUpdated.Headers = new();
userUpdated.Headers.Add("TraceId", HttpContext.TraceIdentifier);
_eventPublisherManager.Publish(userUpdated);

Reading property from the subscribed event's headers

We can read the attached property value from the Headers collection of the received event. Example:

public async Task<bool> Receive(UserCreated @event)
{
    if (@event.Headers?.TryGetValue("TraceId", out var traceId) == true)
    {
    }

    return await Task.FromResult(true);
}

Changing a naming police for serializing and deserializing properties of Event

By default, while serializing and deserializing properties of event, it will use the PascalCase, but you can also use CamelCase, SnakeCaseLower, SnakeCaseUpper, KebabCaseLower, or KebabCaseUpper if you want. For this you need to add PropertyNamingPolicy option to RabbitMQSettings section if you want to apply it for all publishers or subscribers, or you can use it only for specific publisher or subscriber event. Example:

"RabbitMQSettings": {
    "DefaultSettings": {
      //your settings
      "PropertyNamingPolicy": "KebabCaseLower"
    },
    "Publishers": {
      "PaymentCreated": {
        //your settings
        "PropertyNamingPolicy": "SnakeCaseUpper"
      }
    },
    "Subscribers": {
      "UserDeleted": {
        //your settings
        "PropertyNamingPolicy": "CamelCase"
      }
    }
  }

Setting up the Inbox and Outbox patterns in this library

As mentioned earlier, implementing the Inbox and Outbox patterns with this library is easy. Currently, it supports storing event data only in a PostgreSQL database.

How to use the Outbox pattern in this library?

As you know, the Outbox pattern for storing all outgoing events or messages of application in a database. To use this functionality, first you need to enable the Outbox feature by adding the following section to your AppSettings file.

"InboxAndOutbox": {
    "Inbox": {
      //Your inbox settings
    },
    "Outbox": {
      "IsEnabled": true,
      "ConnectionString": "Connection string of the SQL database"
      //...
    }
  }

The InboxAndOutbox is the main section for setting of the Outbox and Inbox functionalities. The Outbox and Inbox subsections offer numerous options. For a detailed explanation on using these options, go to the options of Inbox and Outbox sections of the EventStorage documentation.

Your application is now ready to use the Outbox feature. Now you can inject the IEventSenderManager interface from anywhere in your application, and use the Send method to publish your event.

public class UserController : ControllerBase
{
    private readonly IEventSenderManager _eventSenderManager;

    public UserController(IEventSenderManager eventSenderManager)
    {
        _eventSenderManager = eventSenderManager;
    }
    
    [HttpPost]
    public IActionResult Create([FromBody] User item)
    {
        Items.Add(item.Id, item);

        var userCreated = new UserCreated { UserId = item.Id, UserName = item.Name };
        //_eventPublisherManager.Publish(userCreated);
        
        var eventPath = userCreated.GetType().Name;
        var succussfullySent = _eventSenderManager.Send(userCreated, EventProviderType.MessageBroker, eventPath);
        
        return Ok(item);
    }
}

Next, add an event publisher to manage a publishing event with the MessageBroker provider. Since the event storage functionality is designed as a separate library, it doesn't know about the actual sending of events. Therefore, we need to create single an event publisher to the specific provider, in our use case is for a MessageBroker.

public class MessageBrokerEventPublisher : IMessageBrokerEventPublisher
{
    private readonly IEventPublisherManager _eventPublisher;
    
    public MessageBrokerEventPublisher(IEventPublisherManager eventPublisher)
    {
        _eventPublisher = eventPublisher;
    }
    
    public async Task<bool> Publish(ISendEvent @event, string eventPath)
    {
        _eventPublisher.Publish((IPublishEvent)@event);
        return await Task.FromResult(true);
    }
}

The MessageBrokerEventPublisher is serve for all kinds of events those are sending to the MessageBroker provider. But if you want to create event publisher for the event type for being able to use properties of event without casting, you need to just create event publisher by using generic interface of necessary publisher. In our use case is IMessageBrokerEventPublisher<UserCreated>.

public class CreatedUserMessageBrokerEventPublisher : IMessageBrokerEventPublisher<UserCreated>
{
    private readonly IEventPublisherManager _eventPublisher;

    public CreatedUserPublisher(IEventPublisherManager eventPublisher)
    {
        _eventPublisher = eventPublisher;
    }
    
    public async Task<bool> Publish(UserCreated @event, string eventPath)
    {
        _eventPublisher.Publish(@event);
        
        return await Task.FromResult(true);
    }
}

Since we want to publish our an event to the RabbitMQ, the event subscriber must implement the IMessageBrokerEventPublisher by passing the type of event we want to publish. And, inject the IEventPublisherManager interface to publish the publishing UserCreated event to the RabbitMQ. When we use the Send method of the IEventSenderManager to send an event, the event is first stored in the database. Based on our configuration (by default, after one second), the event will then be automatically execute the Publish method of created the CreatedUserMessageBrokerEventPublisher event publisher.

If an event fails for any reason, the server will automatically retry publishing it, with delays based on the configuration you set in the Outbox section.

How to use the Inbox pattern in this library?

As you know, the Inbox pattern for storing all incoming events or messages to the application in a database. To use this functionality, first you need to enable the Inbox feature by adding the following section to your AppSettings file.

"InboxAndOutbox": {
    "Inbox": {
      "IsEnabled": true,
      "ConnectionString": "Connection string of the SQL database"
      //...
    },
    "Outbox": {
      //Your inbox settings
    }
  }

And then, set true to the UseInbox option of the RabbitMQSettings.DefaultSettings. Because by default it is disabled.

"RabbitMQSettings": {
    "DefaultSettings": {
        "UseInbox": true
        //your settings
    },
    "Publishers": {
        //your Subscribers
    },
    "Subscribers": {
        //your Subscribers
    }
  }

That's all. Now all incoming events from RabbitMQ are stored in the Inbox table of the database and then execute the Receive method of your event subscriber. See the document of creating event subscriber.

Advanced configuration of the Inbox and Outbox functionalities while registering to the DI services.

Since the library is designed to from multiple places, there is a way to configure the Inbox and Outbox functionalities from the configuration file or while registering to the DI services.

builder.Services.AddRabbitMQEventBus(builder.Configuration,
    assemblies: [typeof(Program).Assembly],
    defaultOptions: options =>
    {
        //Your settings
    },
    eventPublisherManagerOptions: publisherManager =>
    {
        //Your settings
    },
    eventSubscriberManagerOptions: subscriberManager =>
    {
        //Your settings
    },
    eventStoreOptions: options =>
    {
        options.Inbox.IsEnabled = true;
        options.Inbox.TableName = "ReceivedEvents";
        options.Outbox.IsEnabled = true;
        options.Outbox.TableName = "SentEvents";
    }
);

eventStoreOptions - it is an alternative way of overwriting configurations of the Inbox and Outbox functionalities. If you don't pass them, it will use default settings from the AppSettings. About other configurations, you can get information from here.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.5.2 126 8/13/2024
1.5.1 114 8/13/2024
1.5.0 119 8/11/2024
1.0.1 81 8/4/2024
1.0.0 83 8/3/2024