Setting up the application ASP.NET Core

Cloud

Following my previous post of Architecting ASP.NET Core applications, in this post I’m going to explain how setting up the application for ASP.NET Core with clean architecture from scratch.

The source code of this post is on GitHub. Because one single post is too long, I have created the following posts:

Create the project

First, let us set up the solution and add a basic structure already. So I’m going to take you through the creation of the entire application architecture. And I really want to start from the blank slate, so I’ll start from a blank solution. So here in Visual Studio we have a Blank Solution template. I’ll take that one, and I’ll call my solution MyTicket.TicketManagement, and I will click Create.

- Setting up the application ASP.NET Core

I’m not going to be writing any code just yet, but I am already going to prepare a couple of things. I’m going to be adding a couple of solution folders. I’ll bring in a solution folder where I’ll put all my sources, and I’ll do the same for another solution folder that I’ll be using to place all my tests in later on. I’m also going to already under src bring in a couple of more solution folders, one where I’ll put the API, another one that I’ll call Core, another one for the Infrastructure, and finally, one where I output my Blazer application later on, and that will be the UI solution folder. So, it is already in place. We’re ready to start adding projects, which I’ll be doing in the next demo.

Solution folder structure - Setting up the application ASP.NET Core
Solution folder structure

Creating the Domain Project

First step in setting up the application ASP.NET Core is to create the domain level. Then, the domain will definitely be part of the core of the application. Typically, I place the domain in a separate project, and I’m going to do that here too. But the main entities that we’ll create will be POCOs. We need to do so to ensure persistence ignorance. And since will be using Entity Framework Core later on, that won’t be a problem.

New .NET Standard Class Library

In this post, I will add the solution to the project that contains the domain for our application, and we’ll bring in the correct domain entities. So, time to start adding projects to our solution, and I’ll start under Core by adding a new .NET Standard project. .NET Standard allows us to also share code to other technologies, so I’ll use that for all my class libraries here. So, I’ll search for .NET Standard Class Library, of course take the C# version, and I will call this library MyTicket.TicketManagement.Domain, since this will actually contain my domain entities.

Add a new project in Visual Studio 2019 as .NET Standard - Setting up the application ASP.NET Core
Add a new project in Visual Studio 2019 as .NET Standard

Then, I’ll name the project and check if the folder is correct.

Named the project and check the location - Setting up the application ASP.NET Core
Named the project and check the location

So, I’ll decide what .NET Framework I want to use. As said, select from the dropdown list .NET Standard 2.1.

Select target framework for the project - Setting up the application ASP.NET Core
Select target framework for the project

We’ll delete the class, Class1, and we’ll create our own classes. Now remember what we said, we’ll need events, categories, and orders.

Create first entity

So, I’m going to add these entities first. I’ll bring in a folder called Entities, and in there I’ll create a number of classes that you can also find, by the way, in the repository on GitHub. So we’ll start with an event. An event has an ID, a name, a price, and so on, and also has a link to a category. A category has an ID, a name, and also link to events. And finally, we’ll have an order that contains just the number of regular properties, including the ID of the order, the user ID, and so on.

public class Category
{
    public Guid CategoryId { get; set; }
    public string Name { get; set; }
    public ICollection<Event> Events { get; set; }
}

public class Event
{
    public Guid EventId { get; set; }
    public string Name { get; set; }
    public int Price { get; set; }
    public string Artist { get; set; }
    public DateTime Date { get; set; }
    public string Description { get; set; }
    public string ImageUrl { get; set; }
    public Guid CategoryId { get; set; }
    public Category Category { get; set; }
}

public class Order
{
    public Guid Id { get; set; }
    public Guid UserId { get; set; }
    public int OrderTotal { get; set; }
    public DateTime OrderPlaced { get; set; }
    public bool OrderPaid { get; set; }
}

Also, I’ll bring in one more class, and I’m going to put that under a different folder called Common.

Common folder

So, I’m going to bring in, let’s say, a base entity, and I’m going to call that the AuditableEntity. Let’s bring in that class, and you’ll see in a minute what it will do. It will contain a couple of base properties, and I just want every other entity to have full logging for tracking purposes in my data store. Here is my AuditableEntity, which has a CreatedBy and a CreatedDate and a LastModifiedBy and LastModifiedDate.

public class AuditableEntity
{
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string LastModifiedBy { get; set; }
    public DateTime? LastModifiedDate { get; set; }
}

And what I’ll do is I’ll make all my other entities inherit from this base class, and later on we’ll see how we can also automatically fill this in as a way of tracking data. So I’ll make sure that now all the other entities inhabit from that AuditableEntity. For example

public class Category: AuditableEntity
{
    public Guid CategoryId { get; set; }
    public string Name { get; set; }
    public ICollection<Event> Events { get; set; }
}

If you build this and that seems to work fine, we already have the domain project ready.

Designing the Application Project

With the domain part ready, the first part of the core project is ready, but now comes the bigger part. Now, we’ll be spending time creating the application project, part of the core of our application architecture.

Clean architecture schema - Setting up the application ASP.NET Core
Clean architecture schema

Just to be clear again, the application project is part of the core of the application architecture. While creating our application architecture, we have to keep our main goals in sight. Application core must remain loosely coupled and have no links to the implementation parts, part of the rest of the application. We’ll rely heavily for this on contracts, so interfaces, which we’ll start creating in the app core.

Next, as mentioned before, the second concept that will help us in achieving the required level of loose coupling is messaging. And to do messaging between components, we avoid direct concrete references from the one component to the next. Instead, we’ll have an intermediate, a mediator. We’ll bring this one in very soon as well.

Contracts

Contracts first, so abstractions. It, as said, is going to be a fundamental concept while working on the application core. You’ll see that we end up with quite a large number of interfaces in the application core. And yes, that is normal. We’ll create our code against interfaces. In the core we will implement some of these interfaces, but most will actually be in the infrastructure later on.

The first place where we’ll encounter the use of contracts will be repositories. In our application architecture, we’re using the repository pattern. In general, the role of the repository is mediating between the application code and the data mapping layer. It’s a common pattern to use when your application performs data access operations.

Often, the repository is in combination with another pattern, the unit of work. It is debatable whether you should use a repository or not when you’re already using Entity Framework Core with its database contexts since that already fulfils a lot of the functionalities of a repository. I still often use them so I have included them here in our architecture.

Usually, we are using the repository pattern to create a layer of indirection between the consumer of the data and the actual data access code. I think this is really applying the DRY pattern, so the don’t repeat yourself pattern. What you will do is creating a generic repository with generic methods like a generic Add, Remove, and GetByIdAsync.

Of course, it’ll be just the contract that we are creating, not the implementation just yet. Next to a generic repository, we’ll bring in specific repositories where needed. The specific repositories will contain well more specific methods, such as a GetAllTicketSalesForMonth, which is not covered in the generic repository.

Repository code

So, for setting up the application ASP.NET Core it is time to head back to Visual Studio. We’ll bring in the Application project, and we’ll create the first contracts, including the one for the repository. I’ve gone ahead and already created another .NET Standard project here called the MyTicket.TicketManagement.Application project. It’s again a .NET Standard project.

So, in here, we’ll create our Application logic. You’ll see quite a lot of code that goes into this project. So, I’m going to start by bringing in some contracts, and just to organize things, I’m going to create a new folder, and I’m going to call that Contracts.

As said, some contracts will be implemented in the Core, but most contracts will actually be implemented in the Infrastructure. I’m actually going to create some more sub folders here just to organize things. Our application isn’t going to be that big, but I can imagine in a real enterprise application, you’ll have a lot of contracts, so I do recommend to add some extra organization here.

Now, I’ll bring in a folder for Persistence, since Persistence will be the project under Infrastructure, that is where I have put my database interaction code later on. So, I’ve added here the Persistence folder, and in there we’re going to bring in a couple of contracts already. One that we’ll need to do all the interaction with my database is going to be a repository, and I’m going to bring in an interface called the IAsyncRepository.

IAsyncRepository

The IAsyncRepository will be a base interface that is going to contain some generic methods on the repository. Here you see the interface, and let me start by bringing in the correct using statements.

public interface IAsyncRepository<T> where T : class
{
    Task<T> GetByIdAsync(Guid id);
    Task<IReadOnlyList<T>> ListAllAsync();
    Task<T> AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);
    Task<IReadOnlyList<T>> GetPagedReponseAsync(int page, int size);
}

So, what I have here is a declaration of what an AsyncRepository should actually be capable of. It should actually be capable of, in a generic way, getting an entity by ID, everything is going to have a GUID as a key. I’m also going to be able to get a read‑only list to list all entities of a certain type. It should be able to add, update, and delete an entity.

Notice that I’ve also specified here a constraint that T should be a class. Now, with this generic repository, we’re not going to be able to build an entire enterprise application. We are going to need some more specific repository methods, and for that reason, I’m also going to bring in a couple of other repositories specific for the entities in our domain.

Repository implementation

So, for example, I’ll bring in another interface, and it’s going to be the IEventRepository. That will be an interface as well that implements the IAsyncRepository and Event. And it seems that it doesn’t know about Event yet, and that’s actually quite normal because I have not added the reference to Domain project yet, so let’s do that now.

public interface IEventRepository : IAsyncRepository<Event>
{
    Task<bool> IsEventNameAndDateUnique(string name, DateTime eventDate);
}

And we’ll do the same for the OrderRepository and the CategoryRepository. So, we now have the OrderRepository and the CategoryRepository added as well. Of course, they’re just contracts, no implementation yet, that won’t be added here in the Application project anyway, but we’ll need those to actually write our business logic.

public interface ICategoryRepository : IAsyncRepository<Category>
{
    Task<List<Category>> GetCategoriesWithEvents(bool includePassedEvents);
}

public interface IOrderRepository : IAsyncRepository<Order>
{
    Task<List<Order>> GetPagedOrdersForMonth(DateTime date, int page, int size);
    Task<int> GetTotalCountOfOrdersForMonth(DateTime date);
}

So, with that added, you see that we are already adding quite a few contracts in our Application project. We are going to be talking in our Application mainly to these contracts, those abstractions, and we’ll implement those later on in the Persistence and in the Infrastructure project, but that will be something for the next module. Let’s now go back to the slides and see what else we need to add here in the Application project. Here the structure of the solution so far.

Folder structure of the solution - Setting up the application ASP.NET Core
Folder structure of the solution

Using MediatR and AutoMapper

Then, writing only contracts won’t get us very far. We’ll need to write some actual business logic as well. That we will start doing now, and I’ll take you step‑by‑step through how I’ve implemented this.

We’ll start with introducing a mediator, again, with the goal of achieving a high level of loose coupling. We’ve touched on the concept of a mediator already, but let’s now take a look at it in detail.

First, what’s the mediator pattern exactly then? Well, it’s actually a pretty easy pattern. Normally, when we have different objects that need to work together, we’ll create references from the one object to the other, and the next one, and another one. While in essence there’s nothing wrong with this, it creates tight links between them, decreasing the ability to test them in isolation later on, since we’ll have a hard time separating them.

Example of references among objects - Setting up the application ASP.NET Core
Example of references among objects

Using a mediator, we’re introducing an intermediate, an object itself that encapsulates how objects will interact with each other.

Our original objects know the mediator, and when one object needs to say something to another object, it just says this to the mediator, which will inform the interested other objects. No tight links anymore, our original objects just know the mediator. Now you may be thinking, where are we going to introduce this? Well, I’m going to wrap our business logic in objects that will be triggered when a certain request comes in.

Mediator explained - Setting up the application ASP.NET Core
Mediator explained

This will result in loose coupling: from the outside of our application project, a message can just be sent in that contains the request of what needs to be done, and that will be the message. A handler inside of the application project will then trigger, handle the message, effectively executing our business logic. By writing our code this way, it will be much simpler to handle changes.

MediatR

The business logic is totally independent, and when it’s being changed, no other components are impacted. Also, since the business logic is encapsulated, it’s a lot easier to test it also in isolation. This all sounds very nice, but should we then be writing ourselves a mediator? Well, we could do that, but there are open source solutions available, which have a proven track record. What I would like to use here is a package called MediatR.

Install-Package MediatR

The GitHub page states that the library is indeed a simple .NET mediator pattern implementation, and that’s exactly what we need. To start using MediatR in our application project, we simply add a NuGet package and we’ll need to register it in the ConfigureServices of our API later on. The library is very straightforward to use.

Create the first handler

As said, we’ll need just two parts, a message that contains what we need to do, and the handler, which will contain a logic to handle the message. In MediatR, we have the IRequest interface for wrapping the message, and the IRequestHandler for handling a specific type of message.

public class GetEventsListQueryHandler : 
    IRequestHandler<GetEventsListQuery, List<EventListVm>>
{
    public async Task<List<EventListVm>> Handle(GetEventsListQuery request, 
        CancellationToken cancellationToken)
    { }
}

Assume that we’re writing the business logic that will return the list of all events in MyTicket. Some object, and this will be the controller, as you’ll see later on, will request that we get a list of all events. This is inter‑object communication, so that will be a message. I’ll thus create an object that wraps this message, which is nothing more than a class that implements that IRequest interface. It’s a generic interface, the type parameter points to the type of data being returned. So this is the message. How will it be handled then?

Creating an handler for MediatR

Quite simply, we’ll need to write a handler of a given type. That will be here this class which is the request handler for the IRequest type we just created. Creating a request handler is done using a class that implements the IRequestHandler interface, and the type parameter is the IRequest it handles.

When the message is received, the interface method Handle will be called. And in there, we’ll write the logic to handle the message. In our case, this will return, using the repository probably, the list of all events. A MediatR is a very nice library that contains even more functionality, but we’re not using all of it. The reason is that I want to do some things, well, manually, let’s say. But if you’re interested in more, take a look at its more advanced capabilities, including the pipeline behaviours to add automatic logging, validation, and caching. Then, to summarize, the advantage of a mediator are:

  • Changes can be handled easily
  • Easy to test the object

AutoMapper

Now, I need to introduce one more library that will make our lives easier. Our application layer will get back from the repository, well, entities. But I don’t want to pass entities to the consumer, right? We’ll create separate objects that contain just the properties we need, referred to as view models. But I’m not interested in writing manually the code to create this new object and transfer all the data from property to property.

So, the good news is that there is another great library called AutoMapper that will do this for us. As the name gives away, it does exactly what it says it will do, namely, avoiding that we need to write mapping code for one object to another. AutoMapper is open source and also free to use and just takes an object and will automatically create a target object and fill the properties with the values. Most of it goes automatically, so, for example, if the properties have the same name. But it is also flexible enough to allow you to write extra logic that contains more complex mapping logic.

Example how to call AutoMapper

Using AutoMapper is, just like MediatR, pretty simple. You bring in a NuGet package called AutoMapper. Next, you register it in the Startup.cs, so in the services collection. Finally, you need to specify to it which types it should be mapping to which. This happens in a so‑called profile.

var result = _mapper.Map<List<EventListVm>>(allEvents);

Here’s a simple example of using AutoMapper. Mapper is here an instance of AutoMapper. And we pass it an object, in our case, allEvents. That’s a list of entities, and these now need to be mapped to a list of view models. Using mapper.Map, the view model object will be instantiated, and the data of the properties is copied over.

Writing the Application Logic in the Request Handler

We have now already covered quite a few concepts. Let’s head back to Visual Studio and see how we can make this happen. We’ll bring in MediatR and AutoMapper. We’ll then create a request and a request handler and we’ll prepare things so that our libraries can be registered in the service collection. So, we are going to now together write the business logic to get a list of all events. Then, to write our business code, I’m going to need a couple of packages. I’m actually going to be using AutoMapper and MediatR. AutoMapper will be used to map between entities. MediatR will be used to write those requests and request handlers, so the messages and the message handlers, that is.

<ItemGroup>
  <PackageReference Include="AutoMapper" Version="10.1.1" />
  <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
  <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
</ItemGroup>

So, in the NuGet Package Manager I’ll add these packages (see the ItemGroup above). So what I’ll do now is I’ll wrap what I’m going to do in an object. That is going to be my request. So, what I’ll do now is I’ll wrap what I want to do, that would be getting all the events in a list in an object. So, this is just going to be let’s say the message of what I want to do. I want to get a list of all events. So I’m going to wrap that in a class in an object, and that is going to be called the GetEventsListQuery. I’m calling it query because I want to retrieve something, and what I want to retrieve is a list of all events. I’ll create this class under a new folders called Features and then Events.

Implement GetEventsListQuery

So, I need to make this class a message, and that I’m going to do by letting it implement the IRequest interface. The parameter, the type parameter, that is, for IRequest is going to be the type of data that this query is going to be getting back, and it’s going to be a list of EventListVms. That is another class that I’m going to be returning.

public class GetEventsListQuery : IRequest<List<EventListVm>>
{
}

I’m going to create a specific object, a view model, that I’m going to return for my client application. It’s going to be a view model that is going to contain just the properties to visualize in a list, just enough information so that I don’t return too much data.

Now, I just add that class because I haven’t created that. So I’m going to create a new class, and it’s going to be the EventListVm, and it’s going to be containing, like I said, the data that I want to visualize for an event when shown in a list. It contains just a number of base properties, not all the properties of event, but just the ones I’ll need in the list view. And now this query here is also satisfied.

public class EventListVm
{
    public Guid EventId { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public string ImageUrl { get; set; }
}

So, this is the message. Now, I also need a message handler that is going to be triggered let’s say when this message is being sent. And that is going to contain the actual business logic.

Implement GetEventsListQueryHandler

Then, that handler is going to be triggered by MediatR. Now, I’m going to create another class, and that is going to be the GetEventsListQueryHandler handling the GetEventsListQuery That is going to be another class that now needs to implement another interface from MediatR.

public class GetEventsListQueryHandler :
    IRequestHandler<GetEventsListQuery, List<EventListVm>>
{
}

So, I’m specifying here that this implements the IRequestHandler for GetEventsListQuery, that’s my message type, and it is going to be returning the list of EventListVMs. I still need to implement the method where I’m going to handle the message, and that method is effectively called Handle.

So, this method will be called automatically when a GetEventsListQuery is fired off, and this handler will pick it up, so that’s going to contain my business logic. Let me implement that here.

public class GetEventsListQueryHandler : 
    IRequestHandler<GetEventsListQuery, List<EventListVm>>
{
    private readonly IAsyncRepository<Event> _eventRepository;
    private readonly IMapper _mapper;

    public GetEventsListQueryHandler(IMapper mapper, IAsyncRepository<Event> eventRepository)
    {
        _mapper = mapper;
        _eventRepository = eventRepository;
    }

    public async Task<List<EventListVm>> Handle(GetEventsListQuery request, 
        CancellationToken cancellationToken)
    {
        var allEvents = (await _eventRepository.ListAllAsync()).OrderBy(x => x.Date);
        return _mapper.Map<List<EventListVm>>(allEvents);
    }
}

Now, if I base it in a bit of code, let me first make sure that all the using statements have been added correctly, so what does this code now do? Well, let me take you through it. In the constructor, I’m getting in a mapper, that is AutoMapper, as you can see here. I’m also going to be using an IAsyncRepository in events. Indeed, this query handler is my business logic and is going to work with the repositories to get the list of events.

Injection

Now, to construct the injection, I’m going to get an instance of both eventRepository and AutoMapper. Now, the actual magic is happening here in the Handle method as set. I’m going to use my eventRepository. I’m going to actually use the ListAllAsync method, which we defined on the IAsyncRepository, so the base interface. And I’m going to get all the events ordered in by date.

That gives me a list of entities, an IOrderedEnumerable of events, so my domain entities. I don’t want to return entities to my client. I want to return objects that I’m in control of that only contain the properties I want to return, and those are available on my EventListVm.

Now, I don’t want to write mapping code myself, so I’m going to use AutoMapper for that. And in AutoMapper, I can use the Map method here as defining the type I want to map to, that is, the list of EventListVms, and I’m going to use allEvents as the object that I want to be mapping from.

So, what is going to be returned is a list of event list view models. Now, AutoMapper does need some more information for this to actually work. It does need what is known as a profile.

Add an AutoMapper profile

A profile will contain mapping information so that AutoMapper knows I should actually be able to try to map from one type to another.

AutoMapper does a lot of work automatically, but sometimes you’ll need to help it a bit. But if the properties on the EventListVm have the same names as the ones on the actual entity, then it will do the mapping itself. If we then just create a profile, the mapping will be done automatically for us, if we define that in a profile, that is. So I’m going to go to my application project, and I’m going to create a folder called Profiles. Now, a Profile is really nothing to be scared of, it is just a simple class that contains what AutoMapper should be knowing in terms of mappings between types.

I’m going to call this the MappingProfile class. It is, like I said, just a class, and it does need to inherit from the base Profile class. That is a class that comes with AutoMapper. In the constructor, I’m going to write my mappings. So, I’m going to create a mapping between Event and EventListVm, and I’m going to specify that it has to be capable of knowing about this mapping in the two ways, so from Event to EventListVm, and vice versa.

public class MappingProfile: Profile
{
    public MappingProfile()
    {
        CreateMap<Event, EventListVm>().ReverseMap();
    }
}

That I’m doing here using the ReverseMap. Let me bring in the correct using statements for both Event and EventListVm. We’ll add more mapping profiles as we go along. I want to point out one more thing here.

IRequest for EventDetailVm implementation

Now, as we can see in the code above in GetEventsListQuery, I’ve used the IAsyncRepository in event. Of course, we don’t have an implementation yet. Basically, that’ll be plugged into dependency injection the implementation of dependency version, later on. We’ll still need to write an actual repository that knows how to handle this in the database. But we don’t have that yet, but you can see that in the application object, we’re just talking with the abstraction. That’s what application project is going to be doing, just talking with the abstractions.

So, I think at this point my GetEventList implementation is more or less ready. But I’m going to bring in yet another one getting the event details, and I’ll show you the result of that. I’ve now brought in a few extra classes which are going to be used to get the event details. So I have here another query, GetEventDetail, which is going to be a little bit different compared to our GetEventListQuery in a sense that they GetEventDetailQuery contains an extra parameter. I need to know which event detail do I need to fetch? So that I’ve wrapped here in the GUID ID.

public class GetEventDetailQuery : IRequest<EventDetailVm>
{
    public Guid Id { get; set; }
}

Notice that the IRequest is now going to be a generic in EventDetailVm. So the return type is now going to be a different VM, a different view model, that contains more properties about the event, event detail information, that is.

public class EventDetailVm
{
    public Guid EventId { get; set; }
    public string Name { get; set; }
    public int Price { get; set; }
    public string Artist { get; set; }
    public DateTime Date { get; set; }
    public string Description { get; set; }
    public string ImageUrl { get; set; }
    public Guid CategoryId { get; set; }
    public CategoryDto Category { get; set; }
}

Category implementation

So, notice I also have a nested entity called CategoryDto. Also, I need to know information about the category of that event. That’s a different type. It’s again not the actual Category entity that I’m going to be returning, I’m going to be returning a nested CategoryDto containing the ID and the name of the category that I need to return for my event detail.

public class CategoryDto
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

GetEventDetailQueryHandler implementation

Now, the GetEventDetailQueryHandler is also included here, and I’ll take you through that one as well. It’s again an IRequestHandler now going to be triggered by the GetEventDetailQuery and is going to return not the list of EventListVms, but a single EventDetailVm. We’re again going to use the EventRepository, but we’re also going to use a CategoryRepository and we’re also going to be using AutoMapper again. The constructor is going to be using, again, dependency injection to get later on concrete implementations of this repository plugged in.

GetEventDetailQueryHandler code

public class GetEventDetailQueryHandler : 
        IRequestHandler<GetEventDetailQuery, EventDetailVm>
{
    private readonly IAsyncRepository<Event> _eventRepository;
    private readonly IAsyncRepository<Category> _categoryRepository;
    private readonly IMapper _mapper;

    public GetEventDetailQueryHandler(IMapper mapper, 
        IAsyncRepository<Event> eventRepository, 
        IAsyncRepository<Category> categoryRepository)
    {
        _mapper = mapper;
        _eventRepository = eventRepository;
        _categoryRepository = categoryRepository;
    }

    public async Task<EventDetailVm> Handle(GetEventDetailQuery request, 
        CancellationToken cancellationToken)
    {
        var @event = await _eventRepository.GetByIdAsync(request.Id);
        var eventDetailDto = _mapper.Map<EventDetailVm>(@event);
            
        var category = await _categoryRepository.GetByIdAsync(@event.CategoryId);
        eventDetailDto.Category = _mapper.Map<CategoryDto>(category);

        return eventDetailDto;
    }
}

Handler Explanation

In the Handle method, which is going to be triggered when that message is received, I’m going to get in the GetEventDetailQuery, that’s the one we just saw, containing the ID of the event I want to get the information of. So, I’m again going to use my eventRepository, so my generic repository, GetByIdAsync, passing in the ID. That is going to return, in this case, an EventDetailVm, at least that’s what I want. So, I’m going to again use AutoMapper to map to that target type. Now, remember that I also had in here that CategoryDto. That is not going to be returned automatically by my GetByIdAsync for an event.

Mapping implementation

So, I’ll need my categoryRepository to get the event’s category, and that I’m doing here. Then, I use another mapping to map a Category entity to a CategoryDto, and then I finally return my EventDetail. Now this will work fine, this will actually compile, that’s very fine, there we go, but we also need to add a few more mapping because I’ve added here a mapping from Event to EventDetailVm and another mapping from Category to CategoryDto. Now, I need to go back to my mapping profile and I’ll need to bring in a mapping from Event to EventDetailVm, and from Category to CategoryDto.

public MappingProfile()
{
    CreateMap<Event, EventListVm>().ReverseMap();
    CreateMap<Event, EventDetailVm>().ReverseMap();
    CreateMap<Category, CategoryDto>();
}

With those in place, this will actually run fine. Now, we need to do one more thing. I’ve used in my application project now MediatR and AutoMapper. These two packages need to be registered with the service collection of my application. Now, we don’t have direct access to the service collection insight of my ASP.NET Core application that we’ll add later on.

Add extensions

First, what I’ll do is I’ll bring in a new class that is going to add an extension on top of this service collection class, so an extension method, that is. Then, I’m going to call this the ApplicationServiceRegistration, and I’m going to add an Extension method here.

public static class ApplicationServiceRegistration
{
    public static IServiceCollection AddApplicationServices(this IServiceCollection services)
    {
        services.AddAutoMapper(Assembly.GetExecutingAssembly());
        services.AddMediatR(Assembly.GetExecutingAssembly());

        return services;
    }
}

Now, I’ve made this into an Extension method on IServiceCollection, and I’m then registering AutoMapper and MediatR on the services collection. Of course, that is returning services to the caller. This we’ll need later on in my ASP.NET Core application to make sure that AutoMapper and MediatR have been correctly registered. With this in place, let’s do another build, and that seems to work fine.