Scroll Top

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 ExecuteResultRequest<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>>());
           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:

options => options.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:

  • With no result:
    • ExecuteUnitRequest<TWebApi>: execute any method from TWebApi
    • ExecuteUnitRequest<TWebApi, TModelData, TApiData>: execute any method from TWebApi with TModelData mapped* with TApiData
  • With result:
    • ExecuteResultRequest<TWebApi, TApiData>: execute any method from TWebApi with a TApiData result
    • ExecuteResultRequest<TWebApi, TModelData, TApiData>: execute any method from TWebApi with a TApiData mapped* to a TModelData result
    • ExecuteResultRequest<TWebApi, TModelResultData, TApiResultData, TApiRequestData, TModelRequestData>: execute any method from TWebApi, sending TApiRequestData mapped from TModelRequestData, then returning TModelResultData mapped from TApiResultData

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

witch ends to something like:

var userList = await _mediator.Send(new ExecuteResultRequest<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 two typed mediator available for each api interface (classic or CRUD), to help you write things shorter.

For example, with classic apis, resolving/injecting IApizrMediator<TWebApi> gives you access to something shorter like:

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

With CRUD apis, resolving/injecting IApizrCrudMediator<TApiEntity, TApiEntityKey, TReadAllResult, TReadAllParams> gives you access to something shorter like:

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 all the ways of using MediatR with Apizr (choose the one you want):
public class MyViewModel
{
    private readonly IMediator _mediator;
    private readonly IApizrMediator _apizrMediator;
    private readonly IApizrMediator<IReqResService> _reqResMediator; 
    private readonly IApizrCrudMediator _apizrCrudMediator;
    private readonly IApizrCrudMediator<User, int, PagedResult<User>, IDictionary<string, object>> _userMediator;
    
    public MyViewModel(IMediator mediator, 
        IApizrMediator apizrMediator,
        IApizrMediator<IReqResService> reqResMediator,
        IApizrCrudMediator apizrCrudMediator,
        IApizrCrudMediator<User, int, PagedResult<User>, IDictionary<string, object>> userMediator)
    {
       _mediator = mediator;
       _apizrMediator = apizrMediator;
       _reqResMediator = reqResMediator;
       _apizrCrudMediator = apizrCrudMediator;
       _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 ExecuteResultRequest<IReqResService, UserList>(api => api.GetUsersAsync()));
            users = userList.Data;

            // Or the classic api interface way with Apizr mediator 
            var userList = await _apizrMediator.SendFor<IReqResService>(api => api.GetUsersAsync()); 
            users = userList.Data;

            // Or the classic api interface way with typed mediator
            var userList = await _reqResMediator.SendFor(api => api.GetUsersAsync());
            users = userList.Data;
            
            // Or the crud api interface way
            var pagedUsers = await _mediator.Send(new ReadAllQuery<PagedResult<User>>());
            users = pagedUsers.Data?.ToList();

            // Or the crud api interface way with Apizr mediator 
            var pagedUsers = await _apizrCrudMediator.SendReadAllQuery<PagedResult<User>>(); 
            users = pagedUsers.Data?.ToList();
            
            // Or 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.

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