C
C#4mo ago
Camster

MediatR in Class Library

I have an application that's broken up in 3 parts: API, Blazor, and Class Library. The Class Library contains all of the business logic. I want to start using the Notification pattern supplied by Mediatr to coordinate some of the more complex operations my application performs. However, I'm not sure how to instantiate or even use MediatR in a class library. All the examples on the internet show that you must register MediatR with the IServiceCollection. But my use of MediatR will be strictly within the Class Library, which does not have an IServiceCollection (I use a custom IFactory to manually inject inside the Class Library). I could of course use the one provided by the API and pass it into the Class Library, but after that, I'm not quite sure how the INotification implementations are supposed to be instantiated in the Class Library when they require injections that are internal to the Class Library and not exposed at all to the API.
16 Replies
Camster
Camster4mo ago
Here's an example:
public class AddToLedgerHandler : INotificationHandler<AddToLedgerNotification>
{
private IBudgetBalances _budgetBalances;
private IAccountBalances _accountBalances;
private IBudgetProjections _budgetProjections;

public AddToLedgerHandler(IBudgetBalances budgetBalances, IAccountBalances accountBalances, IBudgetProjections budgetProjections)
{
_budgetBalances = budgetBalances;
_accountBalances = accountBalances;
_budgetProjections = budgetProjections;
}

public Task Handle(AddToLedgerNotification notification, CancellationToken cancellationToken)
{
await _budgetBalances.UpdateBalances(notification.LedgerItems);
await _accountBalances.UpdateBalances(notification.LedgerItems);
await _budgetProjections.UpdateBalances(notification.LedgerItems);
}
}
public class AddToLedgerHandler : INotificationHandler<AddToLedgerNotification>
{
private IBudgetBalances _budgetBalances;
private IAccountBalances _accountBalances;
private IBudgetProjections _budgetProjections;

public AddToLedgerHandler(IBudgetBalances budgetBalances, IAccountBalances accountBalances, IBudgetProjections budgetProjections)
{
_budgetBalances = budgetBalances;
_accountBalances = accountBalances;
_budgetProjections = budgetProjections;
}

public Task Handle(AddToLedgerNotification notification, CancellationToken cancellationToken)
{
await _budgetBalances.UpdateBalances(notification.LedgerItems);
await _accountBalances.UpdateBalances(notification.LedgerItems);
await _budgetProjections.UpdateBalances(notification.LedgerItems);
}
}
My application is a financial application for managing budgets. When I add to the ledger, several types of balances need to be adjusted. I want to coordinate these updates using the INotification pattern. The problem is I have no idea how MediatR is supposed to have these three interfaces injected when they are not exposed at all to the API layer. I could of course expose them and their implementations, and have the API's IServiceCollection manage it instead of my own custom factory. But then that makes me wonder if there's any point to having all the business logic in the Class Library.
Sir Rufo
Sir Rufo4mo ago
Inside the class library write an extension method for IServiceCollection that does all the registration and has - of course - access to all internal types. Name that method UseBusinessLogic. Call that extension method from the Api project services.UseBusinessLogic();.
Camster
Camster4mo ago
Thank you very much! I didn’t know these things were possible. I’ll do some research later tonight when I have some free time
Camster
Camster4mo ago
Yes, I see that now. Until now, it never really clicked as to what was going on behind the scene with the IServiceCollection. I hope you don't mind if I ask you another question. I've only ever really used the IServiceCollection in blazor and API projects, where any interfaces I declare are injected for me automatically. How would I use IServiceCollection in the class library to create instances? Do I pass the IServiceCollection object itself into the library? Or does it compile to something else? Sorry, I've been doing some googling, and haven't been able to find an answer
Sir Rufo
Sir Rufo4mo ago
You resolve from IServiceProvider - the ServiceCollection is for setup that provider It is possible to inject IServiceProvider but you should not. f.i. if you need to create some HttpClient instances, then inject an IHttpClientFactory and use that for creation.
Camster
Camster4mo ago
So in my API, I just inject the IServiceProvider like this, and I can use the provider to create instances using the GetService<T>?
private static async Task<Result<List<LedgerEntity>>> Ledger_Get(IServiceProvider provider)
{

}
private static async Task<Result<List<LedgerEntity>>> Ledger_Get(IServiceProvider provider)
{

}
Sir Rufo
Sir Rufo4mo ago
Yes, but you should not
Camster
Camster4mo ago
Hmm, okay. Oh, is it GetRequiredService<T>?
Sir Rufo
Sir Rufo4mo ago
Because you know what services you need, then inject that services instead of IServiceProvider
Camster
Camster4mo ago
Oh I see what you mean. That will be some serious change with how I'm doing things on my end, but I'm fine making it. Thank you, you've been a big help @Sir Rufo !
Sir Rufo
Sir Rufo4mo ago
👍
statto1974
statto19744mo ago
Also worth noting you would only need to use the MediatR.Contracts package in your class library and inject IMediator into your classes
Camster
Camster4mo ago
You mean, by using MediatR’s request/response and notification systems, MediatR will handle any surplus injections?
statto1974
statto19744mo ago
Yeah basically inject it into the constructor
public class MyClass
{
private readonly IMediator _mediator;

public MyClass(IMediator mediator)
{
_mediator = mediator;
}

public async Task DoSomething()
{
await _mediator.Send(new DoSomethingCommmand());

await _mediator.Publish(new DoSomethingNotification());
}
}
public class MyClass
{
private readonly IMediator _mediator;

public MyClass(IMediator mediator)
{
_mediator = mediator;
}

public async Task DoSomething()
{
await _mediator.Send(new DoSomethingCommmand());

await _mediator.Publish(new DoSomethingNotification());
}
}
Camster
Camster4mo ago
Excellent. It’s all becoming clear to me now. Thank you 😊