Creating the Infrastructure project

Cloud

Following my previous post of Adding Validation using Fluent, my focus in this post will be on creating the Infrastructure project following clean architecture. We are going to bring in yet another layer, another concentric circle around the core, and that will be the infrastructure.

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

I’m going to go through a small introduction of the goal of the infrastructure project first to make sure you understand correctly what we’ll be adding here. Then, we’ll basically be adding two new projects. First, we’ll add persistence, and I’ll do that using Entity Framework Core. Next, I’ll bring in yet another project where I’ll bring in support for other infrastructure tasks.

Understanding the Goal of the Infrastructure Projects

So, let me first try to explain exactly what the goal of the infrastructure layer is and what types of code you should be expecting here in this layer. The core projects contained the business logic, but no mechanisms. And that’s what you get with all these abstractions.

Clean architecture circle - Creating the Infrastructure project
Clean architecture circle

But indeed, we need to implement somewhere the code to write to the database. Well, that will go in the infrastructure layer. If you look back at our schema that we’ve been using so far, now we’re going to focus on this very layer here.

What is in the Infrastructure project?

Remember that the core project should never contain references to Entity Framework Core packages, logging libraries, and the like. Indeed, they should be added and used in the Infrastructure project. Basically, as soon as we introduce a mechanism, an implementation, look at the infrastructure project. Database code will go in here. In our case, we will use Entity Framework while all that code will go in the infrastructure project. So that means the the DbContext and the migrations, also code that interacts with files.

Then, reading from or writing to a file, that will go in here too. Say that you want to use Azure Service Bus in your application, you shouldn’t add Service Bus messaging code in the core. You add an interface in the core, and the implementation, it goes here.

If you want to interact with another API, you’ll typically write a service client. That will go in here too. Logging implementations, same thing. They’ll also live here. I hope you get the message. Anything external goes in the infrastructure project. Make sure to keep the core clean. Now how does this then work? Well, we added a lot of contracts in the core. We saw, for example, an IAsyncRepository. This repository will require an implementation, and that will go in the infrastructure project.

Many interfaces, contracts defined in the core, are implemented in the infrastructure layer. Infrastructure projects will reference the core projects, and it’s through dependency inversion that actual implementation will get plugged into the core from the infrastructure at runtime. Dependency injection, basically the implementation of dependency inversion, is used for this.

What is in the Infrastructure project in brief…

So, what creating the Infrastructure project means in practice? Here the short list.

  • All external or I/O components
  • Configuration of technical frameworks
  • Implementation for contracts defined in the Core project
  • Reference to Core project
    • Through dependency inversion and dependency injection, services get implemented

Adding Data Persistence with Entity Framework Core

Now, that we know the goal of creating the Infrastructure project, let’s get to it and add the first one. Finally, I’ll bring in data persistence using Entity Framework Core. The infrastructure project itself will be just a plain .NET Standard project. Therefore, to make use of Entity Framework we’ll need to follow a few steps, but they’re not any different from what we normally need to do to bring in Entity Framework Core.

So, we’ll need to bring in some NuGet packages first. Then we’ll create a DbContext. And in the DbContext, I’ll create the DbSets for all entities I want to have Entity Framework Core manage. Then, we’ll create a migration. Finally, I need to also register in the Startup.cs, so in the services collection, and I’ll be using Entity Framework Core.

Of course, remember that we created, in the previous post, a contract for our generic repository, the IAsyncRepository. Now, that repository I need to implement here. I’ll create a base repository implementation for methods defined in the contract, making use of the Entity Framework Core DbContext.

public class BaseRepository<T> : IAsyncRepository<T> where T : class
{
    protected readonly MyTicketDbContext _dbContext;

    public BaseRepository(MyTicketDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public virtual async Task<T> GetByIdAsync(Guid id)
    {
        return await _dbContext.Set<T>().FindAsync(id);
    }
}

Then, I’ll need specific methods in the repository layer as well, and for that, we’ll have specific repository implementations, each which will also need to be backed again by a contract in the core. Otherwise, the core simply can’t use it.

The class indeed implements the contract defined in the core project, but now I have concrete method implementations. I’m using a DbContext instance. And to get, for example, an instance by ID, I’ll use the generic GetByIdAsync. But as said, we won’t be able to build an entire enterprise application with just a generic repository. We’ll need to be able to do more specific queries. For that, I’ll use more specific repositories. These are going to be specific for a given type.

Category repository

Here you can see the CategoryRepository, which implements an interface defined in the core, but also still inherits from the base repository. I have a method specific to categories implemented here.

public class CategoryRepository : BaseRepository<Category>, ICategoryRepository
{
    public CategoryRepository(MyTicketDbContext dbContext) : base(dbContext)
    {
    }

    public async Task<List<Category>> GetCategoriesWithEvents(bool includePassedEvents)
    {
        var allCategories = await _dbContext.Categories.Include(x => x.Events).ToListAsync();
        if(!includePassedEvents)
        {
            allCategories.ForEach(p => p.Events.ToList().RemoveAll(c => c.Date < DateTime.Today));
        }
        return allCategories;
    }
}

Add references

Now, I’m going to bring in the infrastructure layer. First, we’ll create a new project called Persistence. It will contain the code for storing data in the database. I’ll then also, in this project, configure Entity Framework Core. We have application logic that will do validations on the data and so on.

Then, we have in the Persistence folder a lot of repositories, but they are just interfaces. So, now we’ll start implementing that. Let us go to the Infrastructure folder that we already created, and I’ll add another new project. Again, I’ll select a .NET Standard project, and I’ll bring in the Persistence project, so MyTicket.TicketManagement.Persistence.

Now, for reasons that have to do with the compatibility of packages, I do need to make this .NET Standard 2.1. The persistence project will have to do with storing data, fetching data from my database. So, we do a couple of things, and I’ll bring in a reference to my application project since I’ll be implementing the contracts for the repositories defined in my application project.

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />

Next, I’ll also bring in a couple of NuGet package references, one to Entity Framework Core SQL Server and also to this ConfigurationExtensions package, which we’ll need later as well.

Implement the project

Next step in creating the Infrastructure project is creating the actual logic for the persistence project. We’ll start with deleting Class1. Now the first thing I’ll do is I’ll bring in a database context, the DbContext. It’s going to be a simple class, MyTicketDbContext. That will be my database context. And since we already have brought in the packages for Entity Framework Core, we can use the DbContext base class.

public class MyTicketDbContext : DbContext
    {
        public MyTicketDbContext(DbContextOptions<MyTicketDbContext> options)
           : base(options)
        {
        }

        public DbSet<Event> Events { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Order> Orders { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
           modelBuilder.ApplyConfigurationsFromAssembly(typeof(MyTicketDbContext).Assembly);

            //seed data, added through migrations
            var concertGuid = Guid.Parse("{B0788D2F-8003-43C1-92A4-EDC76A7C5DDE}");
            var musicalGuid = Guid.Parse("{6313179F-7837-473A-A4D5-A5571B43E6A6}");
            var playGuid = Guid.Parse("{BF3F3002-7E53-441E-8B76-F6280BE284AA}");
            var conferenceGuid = Guid.Parse("{FE98F549-E790-4E9F-AA16-18C2292A2EE9}");

            modelBuilder.Entity<Category>().HasData(new Category
            {
                CategoryId = concertGuid,
                Name = "Concerts"
            });
        }
    }

The code is quite long then I invite you to download or look this code on GitHub.

What does my DbContext contain?

Of course, it contains this constructor, and it also contains DbSets for the Event, Category, and Order entities. Then I have overridden the OnModelCreating, and I’m doing two things here. I’m going to apply configurations. Next, I’m also going to use the OnModelCreating to insert some seed data. So, I’ve added a couple of categories. And using the HasData, I’m going to check if that entity already exists in the database. If not, it’s going to be inserted as part of a migration later on. And I’m doing the same thing for events. I’m adding a couple of events here.

Now, let’s focus our attention again on this ApplyConfigurationsFromAssembly. As I mentioned already, I want to leave my domain entities. I don’t want to litter my domain classes with attributes that have to do with how the database should be created. This time not validation, but how the database should be created because that too I can do using attributes.

Event configuration

Instead, I am going to use configuration classes, which will contain code that specify to the ModelBuilder how the database should be constructed. So, I’m going to go and add a new folder here. I’m going to call that Configurations. And in here, I’ll add configuration classes for each of my entities, and I’ll add just one for now. I’ll bring in a class, and I’ll bring in configuration for the event entity, so I’ll call it EventConfiguration. And that’ll contain the code for configuring how the ModelBuilder should handle the event type.

public class EventConfiguration : IEntityTypeConfiguration<Event>
{
    public void Configure(EntityTypeBuilder<Event> builder)
    {
        builder.Property(e => e.Name)
            .IsRequired()
            .HasMaxLength(50);
    }
}

Now, I’ve specified here that I want to have an EntityTypeConfiguration for Event that will specify, in this case, that the name of the event should be required and also have a maximum length of 50. Let’s make sure that all the correct using statements have been included.

SaveChangesAsync

Now, by calling in the OnModelCreating, the ApplyConfigurationsFromAssembly, it will search for all configurations included in the assembly and apply them on the ModelBuilder. And there’s one more thing I want to show you here all the way at the bottom of the DbContext. I’ve also included an override of the SaveChangesAsync.

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
    foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
    {
        switch (entry.State)
        {
            case EntityState.Added:
                entry.Entity.CreatedDate = DateTime.Now;
                break;
            case EntityState.Modified:
                entry.Entity.LastModifiedDate = DateTime.Now;
                break;
        }
    }
    return base.SaveChangesAsync(cancellationToken);
}

Of course, in the SaveChangesAsync, I’m going to call the base, but I’m also going to add some extra code that will update the properties, the tracking properties that is, on the AuditableEntity.

Remember that all my entities inherited from AuditableEntity? Well in here, when an entity is being saved, I’m going to check if it’s being added, and I’m going to update the CreatedDate. And if it’s being modified, I’m going to update the LastModifiedDate.

Implementing the Persistence Layer

So far, creating the Infrastructure project seems a good fun! What do you thing? Now, with this project in a good shape, I’m going to bring in a few more things. First, we need the base repository implementation using the DbContext. Next, we’ll bring in more specific repositories. And let’s not forget that we also will need some more code to make sure everything is correctly registered later in the service collection. Now we already have DbContext. Let us now create implementations for our repositories, and we have to do a few.

So, I’m going to add them in a folder called Repositories. In here, I need to implement the contracts that we have defined in our application project. So I need to bring in an implementation for the async repository, as well as for the individual and specific repositories. I’ll bring in first the implementation for the IAsyncRepository, so that will be the base repository, and that will require using statement to contracts persistence.

Update Base repository

public class BaseRepository<T> : IAsyncRepository<T> where T : class
{
    protected readonly MyTicketDbContext _dbContext;

    public BaseRepository(MyTicketDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public virtual async Task<T> GetByIdAsync(Guid id)
    {
        return await _dbContext.Set<T>().FindAsync(id);
    }

    public async Task<IReadOnlyList<T>> ListAllAsync()
    {
        return await _dbContext.Set<T>().ToListAsync();
    }

    public async virtual Task<IReadOnlyList<T>> GetPagedReponseAsync(int page, int size)
    {
        return await _dbContext.Set<T>()
            .Skip((page - 1) * size)
            .Take(size)
            .AsNoTracking()
            .ToListAsync();
    }

    public async Task<T> AddAsync(T entity)
    {
        await _dbContext.Set<T>().AddAsync(entity);
        await _dbContext.SaveChangesAsync();

        return entity;
    }

    public async Task UpdateAsync(T entity)
    {
        _dbContext.Entry(entity).State = EntityState.Modified;
        await _dbContext.SaveChangesAsync();
    }

    public async Task DeleteAsync(T entity)
    {
        _dbContext.Set<T>().Remove(entity);
        await _dbContext.SaveChangesAsync();
    }
}

Base repository explained

How is going with creating the Infrastructure project? If you have any problem with the code, you have the complete solution on GitHub. If you want to ask any question, please use our forum.

Then, I’ll bring in also the other using statements and also a using statement to Entity Framework Core. So, I’m implementing now the IAsyncRepository, specifying again that the generic T type should be a class. I’m then going to use my MyTicketDbContext, which I’m then going to insert through dependency injection.

We’ll need to think about that a bit more when we add this to the services collection. Well, I’ll come to that later on. Then I have some basic implementations for the GetByIdAsync, which is just using some base Entity Framework Core code to find an entity by ID. In the ListAllAsync, well I’m simply going to get all entities from a given type. I also have an add, an update, and a delete, which are again very simple. I’m going to add an entity to the DbContext, and I’m going to call the SaveChangesAsync, returning also that entity. I have the same thing for the update, as well as for the delete. So that is now my BaseRepository.

Also, I need to add the implementations for the entity‑specific repository. Let’s do that next.

Event repository implementation

Let’s take a look at the EventRespository first. So the EventRepository inherits from the BaseRepository and Event in this case and also implements the IEventRepository. The IEventRepository had that extra method, IsEventNameAndDateUnique, that we used in the validator to check if that name and date combination was unique. If you don’t remember this implementation, take a look to the previous post.

public class EventRepository : BaseRepository<Event>, IEventRepository
{
    public EventRepository(MyTicketDbContext dbContext) : base(dbContext)
    {
    }

    public Task<bool> IsEventNameAndDateUnique(string name, DateTime eventDate)
    {
        var matches =  _dbContext.Events.Any(e => e.Name.Equals(name) && 
                                                e.Date.Date.Equals(eventDate.Date));
        return Task.FromResult(matches);
    }
}

Well, I’m writing some custom code here, which indeed cannot be part of the BaseRepository. But I’m going to use my DbContext checking in my events if an event already exists with the given name and date, and I’m going to return the result of that. In the specific CategoryRepository, I have an implementation for the GetCategoriesWithEvents that we also already looked at in the previous post.

In there, I’m going to not only get the categories, but for all categories, I’m also going to include all events. That’s why I’m using here the .Include. If I don’t want the events in the past, I’m going to remove the ones that are in the past.

Order repository implementation

I’ve also implemented the OrderRepository, which contains a couple of extra methods that we’ll need to use later on. But they are already here. And again, the repository implementations can all be found in the source code on GitHub. Now I’ve added Entity Framework Core. I’ve added my DbContext.

public class OrderRepository : BaseRepository<Order>, IOrderRepository
{
    public OrderRepository(MyTicketDbContext dbContext) : base(dbContext)
    {
    }

    public async Task<List<Order>> GetPagedOrdersForMonth(DateTime date, int page, int size)
    {
        return await _dbContext.Orders.Where(x => x.OrderPlaced.Month == date.Month && 
                                                x.OrderPlaced.Year == date.Year)
            .Skip((page - 1) * size).Take(size).AsNoTracking().ToListAsync();
    }

    public async Task<int> GetTotalCountOfOrdersForMonth(DateTime date)
    {
        return await _dbContext.Orders.CountAsync(x => x.OrderPlaced.Month == date.Month && 
                                                    x.OrderPlaced.Year == date.Year);
    }
}

Persistence Service Registration

Next step in creating the Infrastructure project is to implement the persistence service registration. What is it? If you think about a regular a ASP.NET Core application, what do we still need to do? We need to let my service collection of my ASP.NET Core application know that we are going to work with EF Core, and that we typically do in the Startup.cs. But of course, we don’t have a Startup.cs. We’re just working with the .NET Standard library here.

So again, like we did in the application, I’m going to bring in a class that extends my service collection. Then, I’m going to bring in a class. This time I’m going to call it PersistenceServiceRegistration where I am going to again extend the service collection, allowing me to make my service registrations from my persistence project.

Again, it’s important that I do that from the persistence project. Here is the PersistenceServiceRegistration static class, which is going to extend again this service collection. Let’s bring in the correct using statements. Look out when you bring in the IConfiguration one because AutoMapper also has a configuration namespace.

PersistenceServiceRegistration code

public static class PersistenceServiceRegistration
{
    public static IServiceCollection AddPersistenceServices(this IServiceCollection services, 
        IConfiguration configuration)
    {
        services.AddDbContext<MyTicketDbContext>(options =>
            options.UseSqlServer(configuration.GetConnectionString("MyTicketTicketManagementConnectionString")));

        services.AddScoped(typeof(IAsyncRepository<>), typeof(BaseRepository<>));

        services.AddScoped<ICategoryRepository, CategoryRepository>();
        services.AddScoped<IEventRepository, EventRepository>();
        services.AddScoped<IOrderRepository, OrderRepository>();

        return services;    
    }
}

Persistence Service Registration explained

So, on the service collection, I’m going to register like normal the DbContext, passing in our own MyTicketDbContext. And I’m already also specifying that I’m going to use SQL Server, passing in the connection string.

Now, we don’t have that connection string yet. That’ll come later when we add the API. At this point, we don’t have an app settings yet. That lives in the executing assembly, and that will be the API. I’m also registering the BaseRepository, as well as the custom repository. And just like with blank Entity Framework Core, we need to do this in a scoped way so that one instance is created per request.

So, with this in place, I think we’re in good shape already with our persistence project. Let’s do a quick build to make sure that we haven’t made any mistakes, and that seems to work fine.

Adding the Infrastructure Project

So, we are quite close to finish creating the Infrastructure project. We want to send an email when an event is created. We’ll face this request in a clean architectural way!

When a new event is being created, we’ll need to have the system send an email to a given address. There are a lot of implementation for that, you can have a look at Integration with Identity in AdminLTE project. But, I think now that we are creating the infrastructure project, this is really a good time to see how this can be added to the architecture. Let’s take a look.

Sending an email

Now, I’m going to bring in support for other infrastructure tasks. Let me show you how we can add that to the architecture. We want that if the system will be able to send an email when an event is being created. Sending an email is definitely infrastructure code. It’s an external system we need to work with.

When I get this request, I typically use SendGrid, an excellent system for sending emails. SendGrid offers an API and a NuGet package. You register at SendGrid, you’ll get a key, and then you can, from code, send emails. No internal mail server is required. Since this new email functionality will need to be called from our core code, we’ll bring in an abstraction for sending emails, which will be used from the core code. Then, we’ll need to write an implementation in the infrastructure again, which will at runtime be plugged in.

Adding Support for Mailing from the Infrastructure

All right, let’s see this end‑to‑end example of bringing in a new requirement, and you’ll see how easy this now has become. This new functionality I’ll place in a general infrastructure project, so separate from the Persistence project, just to keep things cleaner. We’ll then write a logic to interact with SendGrid, and we’ll use this code on the Core code. Finally, we’ll need to register this new service also in the ServiceCollection.

So, we need to send an email and going to bring in a new IEmailService that will be able to send an email. So in the application project, let us go back to Contracts. And in here I’ll now bring in a new folder, and I’ll call that Infrastructure because this has nothing to do with Persistence. This really is infrastructure. So I’ll bring in an Infrastructure folder, and in here I’ll bring in a new contract and I’ll call that the IEmailService.

public interface IEmailService
{
    Task<bool> SendEmail(Email email);
}

This will be my interface. That is just going to know that it needs to send an email. That email type, that will be the thing that I’m going to send, I still need to define that as well. Now for that, I’m going to create a custom model.

Email class

Now, this is part of the application logic, and it will be the type that contains the properties of the email that needs to be sent. So in here, I’ll now go ahead and create a Models folder. I’ll create a subfolder here as well. Let’s call it Mail, and in here I’ll create a new class, Email.

public class Email
{
    public string To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}

That class Email will contain the properties that contain the email message information, the to, the subject, and the body.

Now, you may be thinking, hey, why did he not put that email type under domain? Because this is a class, right? Well, it’s a class indeed, it’s a type, but it has nothing to do with the domain that MyTicket works with. This is just a class, a type that I’ll need to define under my application project for my email service to work correctly. And so, I go to my IEmailService again, and I’ll bring in the using statement.

Email settings

So now we can, in our application logic, in the application project, send an email. We create an email, and then we call SendEmail on that. And again, we’ll need to implement that in an infrastructure project, and I’ll do that in just a second. Now, I’m going to create another type under Mail and it’s going to be EmailSettings.

Now, EmailSettings is going to be a class that contains properties that I’m going to read out from settings later on. Settings, defined in an appsettings file, to configure my external email service, which will be SendGrid. So these values will be read out from an appsettings file later on in the API.

public class EmailSettings
{
    public string ApiKey { get; set; }
    public string FromAddress { get; set; }
    public string FromName { get; set; }
}

Send an email when an event is created

So, now I’m going to implement the application logic that is again totally agnostic of how we are going to send the email. So we want to send an email when an event was created. Well, that’s pretty easy to find. We go to Features, Events, and in there we have the Commands, CreateEvent.

public class CreateEventCommandHandler : IRequestHandler<CreateEventCommand, Guid>
{
    private readonly IEventRepository _eventRepository;
    private readonly IMapper _mapper;
    private readonly IEmailService _emailService;

    public CreateEventCommandHandler(IMapper mapper, 
        IEventRepository eventRepository, 
        IEmailService emailService)
    {
        _mapper = mapper;
        _eventRepository = eventRepository;
        _emailService = emailService;
    }

    public async Task<Guid> Handle(CreateEventCommand request, 
        CancellationToken cancellationToken)
    {
        var validator = new CreateEventCommandValidator(_eventRepository);
        var validationResult = await validator.ValidateAsync(request);
            
        if (validationResult.Errors.Count > 0)
            throw new Exceptions.ValidationException(validationResult);

        var @event = _mapper.Map<Event>(request);

        @event = await _eventRepository.AddAsync(@event);

        //Sending email notification to admin address
        var email = new Email() { 
            To = "info@s981402199.websitehome.co.uk", 
            Body = $"A new event was created: {request}", 
            Subject = "A new event was created" };

        try
        {
            await _emailService.SendEmail(email);
        }
        catch (Exception ex)
        {
        }

        return @event.EventId;
    }
}

CreateEventCommandHandler explained

So, in the CreateEvent command handler, when an event was successfully added, we want to send an email. That’s what I want to do. So, I’m going to bring in the IEmailService, and I’ll also let this be injected through dependency injection as an extra constructive parameter.

Now, when the event was successfully added, I’m going to send an email. I’m going to create that email. I’m going to use my own email, I specify the body and the subject, and then in a try‑catch, I’m going to ask the emailService to send that email. Again, no implementation, just fire and forget, in this case.

In fact, put this in a try‑catch because I don’t want my application to fail should that emailService go wrong. I want to log that, but we’ll come to that later on. So I’m just firing off that email using the emailService with SendEmail. Now, our application logic knows about sending emails.

Add the Infrastructure level

Finally, creating the Infrastructure project! Let us now go to my Infrastructure folder here again, and we’ll bring in yet another project, the actual Infrastructure project. So I’ll go here, and I’ll create another .NET Standard project. I’ll call this MyTicket.TicketManagement.Infrastructure. And again Class1 is there, and let’s delete it.

Now, in the project file, I’ll bring in a couple of references again. This time I’m going to bring in a package to work with SendGrid. I’m going to now implement the real EmailService using SendGrid. SendGrid is an external mail service that offers you the ability to send emails via their service, and have an API that I’m going to work with that. Of course, I don’t want to litter my application project with.

<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />
<PackageReference Include="SendGrid" Version="9.21.1" />

That is really infrastructure code, so that’s why I put this in the Infrastructure project. And least, we forget to add a reference to my application project, because I’ll be implementing the IEmailService here. So, in here I’ll implement the IEmailService, and I’ll bring in a new folder called Mail, and I’ll bring in a class called EmailService. Of cource, this EmailService class will implement my IEmailService interface that lives in the Infrastructure contract.

EmailService code

public class EmailService : IEmailService
{
    public EmailSettings _emailSettings { get; }

    public EmailService(IOptions<EmailSettings> mailSettings)
    {
        _emailSettings = mailSettings.Value;
    }

    public async Task<bool> SendEmail(Email email)
    {
        var client = new SendGridClient(_emailSettings.ApiKey);

        var subject = email.Subject;
        var to = new EmailAddress(email.To);
        var emailBody = email.Body;

        var from = new EmailAddress
        {
            Email = _emailSettings.FromAddress,
            Name = _emailSettings.FromName
        };

        var sendGridMessage = MailHelper.CreateSingleEmail(from, to, subject, emailBody, emailBody);
        var response = await client.SendEmailAsync(sendGridMessage);

        if (response.StatusCode == System.Net.HttpStatusCode.Accepted || 
            response.StatusCode == System.Net.HttpStatusCode.OK)
            return true;

        return false;
    }
}

Also, I need to implement the SendEmail method. Of course, we’re now also are going to be using email, which is part of the Models folder in my application project. So that, I think, is a good place to store that.

Let me implement this SendEmail method. And this EmailService is nothing really complex. It is going to use the EmailSettings. Those were the ones that we defined earlier in our application project under Models. That’ll contain things like the APIKey, the FromAddress, the FromName, that we will also need to put in our appsettings later on.

Then, I implement the SendEmail, which is just going to use the SendGrid API to send the email. I take my APIKey, I create the subject, the from, and then I finally will send the email using that SendGrid client. Again, I need to do one more thing. We again need to create a class that extends the ServiceCollection to register the things we are using inside of this project.

Implement InfrastructureServiceRegistration

So again, we’ll go here and add a new class, and this time I’ll call it the InfrastructureServiceRegistration, and here is the code for that class. I’m again extending the services collection, and I’m registering my EmailService, and I’m also registering that from configuration EmailSettings an instance of EmailSettings should actually be loaded, so those values should be specified later on in the appsettings, but that, again, will be in the API project itself.

public static class InfrastructureServiceRegistration
{
    public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, 
        IConfiguration configuration)
    {
        services.Configure<EmailSettings>(configuration.GetSection("EmailSettings"));

        services.AddTransient<IEmailService, EmailService>();

        return services;
    }
}