Apizr – Part 4: Requesting with Mediator pattern

apizr4
MEDIATOR PATTERN
Apizr offers an integration with MediatR, following the Mediator pattern, for those of you guys using the extended approach.
Mediator pattern ensures to keep all the thing as loosely coupled as we can between our ViewModel/ViewControler and our Data Access Layer. As everything should be loosely coupled between you Views and your ViewModels (MVVM) or ViewControlers (MVC) thanks to data binding, MediatR offers you to keep it all loosely coupled between your VM/VC and your DAL to.
Please read the official documentation to know more about MediatR.
As there will be a dedicated Playground blog post about it, I won’t discuss further what and why here.
The benefit for Apizr to use it is to offer you a very simple and unified way to send your request, no matter from where or about what.
Simple and unified because instead of injecting/resolving each api interface you need to get your data (if you get more than one obviously, but you should), you just have to use the IMediator interface, everywhere, every time.
EXAMPLE
Before MediatR:
This is what using Apizr with classic apis in a ViewModel looks like without MediatR:
public class MyViewModel
{
    private readonly IApizrManager<IReqResService> _reqResManager;

    public MyViewModel(IApizrManager<IReqResService> reqResManager)
    {
        _reqResManager = reqResManager;
    }

    private async Task<List<User>> GetUsersAsync()
    {
        try
        {
           var userList = await _reqResManager.ExecuteAsync(api => api.GetUsersAsync());
           return userList?.Data;
        }
        catch (ApizrException<UserList> e)
        {
           return e.CachedResult?.Data;
        }
    }
}

And the same with CRUD apis:

public class MyViewModel
{
    private readonly IApizrManager<ICrudApi<User, int, PagedResult<User>, IDictionary<string, object>>> _userCrudManager;

    public MyViewModel(IApizrManager<ICrudApi<User, int, PagedResult<User>, IDictionary<string, object>>> userCrudManager)
    {
        _userCrudManager = userCrudManager;
    }

    private async Task<List<User>> GetUsersAsync()
    {
        try
        {
           var pagedUsers = await _userCrudManager.ExecuteAsync(api => api.ReadAll());
           return pagedUsers?.Data?.ToList();
        }
        catch (ApizrException<PagedResult<User>> e)
        {
           return e.CachedResult?.Data?.ToList();
        }
    }
}

After MediatR:

Now classic apis with MediatR:

public class MyViewModel
{
    private readonly IMediator _mediator;

    public MyViewModel(IMediator mediator)
    {
        _mediator = mediator;
    }
    
    private async Task<List<User>> GetUsersAsync()
    {
        try
        {
           var userList = await _mediator.Send(new ExecuteRequest<IReqResService, UserList>(api => api.GetUsersAsync()));
           return userList?.Data;
        }
        catch (ApizrException<UserList> e)
        {
           return e.CachedResult?.Data;
        }
    }
}

And the same with CRUD apis:

public class MyViewModel
{
    private readonly IMediator _mediator;

    public MyViewModel(IMediator mediator)
    {
        _mediator = mediator;
    }
    
    private async Task<List<User>> GetUsersAsync()
    {
        try
        {
           var pagedUsers = await _mediator.Send(new ReadAllQuery<PagedResult<User>>(), CancellationToken.None);
           return pagedUsers?.Data?.ToList();
        }
        catch (ApizrException<PagedResult<User>> e)
        {
           return e.CachedResult?.Data?.ToList();
        }
    }
}

I told you, IMediator could rule them all.

You don’t have to resolve/inject each dedicated api interface anymore.

Just send your request with IMediator and it will be intercepted, handled, and you’ll get your result sent right back to you.

SETUP

In order to use it, please install its dedicated NuGet package called Apizr.Integrations.MediatR.

Then tell it to Apizr by calling:

builder => builder.WithMediation()

and don’t forget to register MediatR itself as usual:

services.AddMediatR(typeof(Startup));
USING

Everything you need to do is sending your request calling:

var result = await _mediator.Send(YOUR_REQUEST_HERE);

Where YOUR_REQUEST_HERE could be:

Classic apis:

  • ExecuteRequest<TWebApi>: execute any method from TWebApi
  • ExecuteRequest<TWebApi, TApiResponse>: execute any method from TWebApi with a TApiResponse result
  • ExecuteRequest<TWebApi, TModelResponse, TApiResponse>: execute any method from TWebApi with a TApiResponse mapped* to a TModelResponse result

* mapped means data mapped with AutoMapper. Please refer to Part 2 blog post.

witch ends to something like:

var userList = await _mediator.Send(new ExecuteRequest<IReqResService, UserList>(api => api.GetUsersAsync()));

CRUD apis:

  • ReadQuery<T>: get the T entity with int
  • ReadQuery<T, TKey>: get the T entity with TKey
  • ReadAllQuery<TReadAllResult>: get TReadAllResult with IDictionary<string, object> optional query parameters
  • ReadAllQuery<TReadAllParams, TReadAllResult>: get TReadAllResult with TReadAllParams optional query parameters
  • CreateCommand<T>: create a T entity
  • UpdateCommand<T>: update the T entity with int
  • UpdateCommand<TKey, T>: update the T entity with TKey
  • DeleteCommand<T>: delete the T entity with int
  • DeleteCommand<T, TKey>: delete the T entity with TKey

witch ends to something like:

var pagedUsers = await _mediator.Send(new ReadAllQuery<PagedResult<User>>());
BONUS

There’s also a typed mediator available for each api interface (classic or CRUD), to help you write things shorter.

With classic apis, resolving/injecting IMediator<TWebApi> gives you access to:

  • SendFor: send an ExecuteRequest<TWebApi> for you
  • SendFor<TApiResponse>: send an ExecuteRequest<TWebApi, TApiResponse> for you
  • SendFor<TModelResponse, TApiResponse>: send an ExecuteRequest<TWebApi, TModelResponse, TApiResponse> for you

witch ends to something as shorter as:

var userList = await _reqResMediator.SendFor(api => api.GetUsersAsync());

With CRUD apis, resolving/injecting ICrudMediator<TApiEntity, TApiEntityKey, TReadAllResult, TReadAllParams> gives you access to:

  • SendReadQuery(TApiEntityKey key): send a ReadQuery<TApiEntity, TApiEntityKey> for you
  • SendReadQuery<TModelEntity>(TApiEntityKey key): send a ReadQuery<TModelEntity, TApiEntityKey> for you, with TModelEntity mapped with TApiEntity
  • SendReadAllQuery(): send a ReadAllQuery<TReadAllResult> for you
  • SendReadAllQuery<TModelEntityReadAllResult>(): send a ReadAllQuery<TModelEntityReadAllResult> for you, with TModelEntityReadAllResult mapped with TReadAllResult
  • SendCreateCommand(TApiEntity payload): send a CreateCommand<TApiEntity> for you
  • SendCreateCommand<TModelEntity>(TModelEntity payload): send a CreateCommand<TModelEntity> for you, with TModelEntity mapped* with TApiEntity
  • SendUpdateCommand(TApiEntityKey key, TApiEntity payload): send an UpdateCommand<TApiEntityKey, TApiEntity> for you
  • SendUpdateCommand<TModelEntity>(TApiEntityKey key, TModelEntity payload): send an UpdateCommand<TApiEntityKey, TModelEntity> for you, with TModelEntity mapped* with TApiEntity
  • SendDeleteCommand(TApiEntityKey key): send a DeleteCommand<TApiEntity, TApiEntityKey> for you

* mapped means data mapped with AutoMapper. Please refer to Part 2 blog post.

witch ends to something as shorter as:

var pagedUsers = await _userMediator.SendReadAllQuery();
CONCLUSION
When Apizr works together with MediatR, we can write things shorter, cleaner and consistent, keeping it all loosely coupled from data to views.
As a reminder, here are the 4 ways of using MediatR with Apizr:
public class MyViewModel
{
    private readonly IMediator _mediator;
    private readonly IMediator<IReqResService> _reqResMediator;
    private readonly ICrudMediator<User, int, PagedResult<User>, IDictionary<string, object>> _userMediator;
    
    public MyViewModel(IMediator mediator, 
        IMediator<IReqResService> reqResMediator,
        ICrudMediator<User, int, PagedResult<User>, IDictionary<string, object>> userMediator)
    {
       _mediator = mediator;
       _reqResMediator = reqResMediator;
       _userMediator = userMediator;
    }
    
    public ObservableCollection<User>? Users { get; set; }

    // This is a dummy example presenting all the ways to play with MediatR
    // You should choose one of it obviously
    private async Task GetUsersAsync()
    {
        IList<User>? users;
        try
        {
            // The classic api interface way
            var userList = await _mediator.Send(new ExecuteRequest<IReqResService, UserList>(api => api.GetUsersAsync()));
            users = userList.Data;

            // The classic api interface way with typed mediator
            var userList = await _reqResMediator.SendFor(api => api.GetUsersAsync());
            users = userList.Data;
            
            // The crud api interface way
            var pagedUsers = await _mediator.Send(new ReadAllQuery<PagedResult<User>>());
            users = pagedUsers.Data?.ToList();
            
            // The crud api interface way with typed mediator
            var pagedUsers = await _userMediator.SendReadAllQuery();
            users = pagedUsers.Data?.ToList();
        }
        catch (ApizrException<UserList> e)
        {
            return e.CachedResult?.Data;
        }
        catch (ApizrException<PagedResult<User>> e)
        {
            users = e.CachedResult?.Data;
        }

        if(users != null)
            Users = new ObservableCollection<User>(users);
    }
}
In this article we’ve seen how Apizr could work with MediatR.
In the next one, I’ll talk about requesting with Mediator and Optional pattern all together to get some exception handling with fluent api.
You’ll find all sources and samples on GitHub.

JeremyBP

Specialized since 2013 in cross-platform applications development for iOS, Android and Windows, using technologies such as Microsoft Xamarin and Microsoft Azure. Initially focused, since 2005, on development, then administration of Customer Relationship Management systems, mainly around solutions such as Microsoft SharePoint and Microsoft Dynamics CRM.

Related Posts

Leave a comment

3 × 5 =