Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

.NET Core 2.0 With Angular 4 and MySQL, Part 4: Repository Pattern

DZone's Guide to

.NET Core 2.0 With Angular 4 and MySQL, Part 4: Repository Pattern

Though this pattern can be tricky at first, once you are familiar with it, it will reduce the amount of redundant code and make the logic much easier to maintain.

· Web Dev Zone ·
Free Resource

Code something amazing with the IBM library of open source blockchain patterns. Content provided by IBM.

With the Repository pattern, we can create an abstraction layer between the data access and the business logic layer of an application. By using it, we are promoting a more loosely coupled approach to access our data from the database. Also, the code is cleaner and easier to maintain and reuse. Data access logic is in a separate class, or sets of classes called a repository, with the responsibility of persisting the application's business model.

Implementing the repository pattern is our topic for this post.

So let's start.

If you want to see all the basic instructions and complete navigation for this series, please follow the following link: Introduction page for this tutorial.

For the previous part check out: Part 3 - Creating .NET Core WebApi project - Custom logging in .NET Core

The source code is available for download at .NET Core, Angular 4 and MySQL. Part 4 - Source Code

Creating Models

Let's begin by creating a new Class Library (.NET Core) project named Entities and inside it create a folder with the name Models, which will contain all of your model classes. Model classes will represent the tables inside the database and will serve you to map the data from the database to the .NET Core. After that, reference this project to the main project.

In the Models folder, create two classes and add the following code:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Entities.Models
{
    [Table("owner")]
    public class Owner
    {
        [Key]
        public Guid OwnerId { get; set; }

        [Required(ErrorMessage = "Name is required")]
        [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Date of birth is required")]
        public DateTime DateOfBirth { get; set; }

        [Required(ErrorMessage = "Address is required")]
        [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")]
        public string Address { get; set; }
    }
}
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Entities.Models
{
    [Table("account")]
    public class Account
    {
        [Key]
        public Guid AccountId { get; set; }

        [Required(ErrorMessage = "Date created is required")]
        public DateTime DateCreated { get; set; }

        [Required(ErrorMessage = "Account type is required")]
        public string AccountType { get; set; }

        [Required(ErrorMessage = "Owner Id is required")]
        public Guid OwnerId { get; set; }
    }
}

As you can see, there are two models decorated with the attributes Table("tableName"). This attribute will configure the corresponding table name in the database. Also, every primary key has the decoration attribute Key that configures the primary key of the table. All mandatory fields have the attribute [Required] and if you want to constrain the strings, you can use the attribute [StringLength]. 

Notice that these models are clean, meaning that they have only those properties that match the columns in the tables. I like to keep models as clean as possible and this is the way to do it. Later on, we will create DTO classes with properties to connect one owner with all of its accounts or to connect a single account with its owner.

Context Class and the Database Connection

Now, let us create the context class, which will be a middleware component for the communication with the database. It has DbSet properties which contain the table data from the database.

In the root of Entities project create the class RepositoryContext and code it like this:

using Entities.Models;
using Microsoft.EntityFrameworkCore;

namespace Entities
{
    public class RepositoryContext: DbContext
    {
        public RepositoryContext(DbContextOptions options)
            :base(options)
        {
        }

        public DbSet<Owner> Owners { get; set; }
        public DbSet<Account> Accounts { get; set; }
    }
}

To enable communication between .NET Core and our MySQL database, you'll need to install a third-party library named Pomelo.EntityFrameworkCore.MySql. In the main project, you may install it with NuGet package manager or Package manager console.

After the installation, open the appsettings.json file and add a DB connection settings inside.

{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "mysqlconnection": {
    "connectionString": "server=localhost;userid=root;password=yourpass;database=accountowner;"
  }
}

In theServiceExtensions class, you are going to write the code for configuring the MySQL context.

First, add the using directives and then add the method ConfigureMySqlContext:

using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;

public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config)
{
    var connectionString = config["mysqlconnection:connectionString"];
    services.AddDbContext<RepositoryContext>(o => o.UseMySql(connectionString));
}

With help of the IConfiguration config parameter, you can access the  appsettings.json file and take all the data you need from it. Afterwards, in the Startup class method ConfigureServices, add your context service to the IOC right above services.AddMvc().

services.ConfigureMySqlContext(Configuration);

Repository Pattern Logic

After establishing a connection with the database, it is time to create the generic repository that will serve us all the CRUD methods. As a result, all the methods can be called upon any repository class in your project.

Furthermore, creating the generic repository and repository classes which use that generic repository is not going to be the final step. You will go a step further and create a wrapper around repository classes and inject it as a service. Consequently, you can instantiate this wrapper once and then call any repository class you need inside any of your controllers. You will understand the advantages of this wrapper when using it in the project.

First, let's create an interface for the repository inside the Contracts project.

namespace Contracts
{
    public interface IRepositoryBase<T>
    {
        IEnumerable<T> FindAll();
        IEnumerable<T> FindByCondition(Expression<Func<T, bool>> expression);
        void Create(T entity);
        void Update(T entity);
        void Delete(T entity);
        void Save();
    }
}

Right after the interface creation, you are going to create a new Class Library (.NET Core) project with the name from Contracts to this project), and, inside the Repository project, create the abstract class Repository (add the reference RepositoryBase which will implement the interface IRepositoryBase).

Reference this project to the main project too.

Tip: If you have problems with referencing EntityFrameworkCore in your Assemblies, for this new Repository project, you need to right click on Repository project, click on Unload Project. When the project unloads, right click on it and choose Edit Repository.csproj. Add this code to the opened file. Finally, save the file and right click on Repository project and click Reload Project.

The RepositoryBase class should look like this:

using Contracts;
using Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Repository
{
    public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
    {
        protected RepositoryContext RepositoryContext { get; set; }

        public RepositoryBase(RepositoryContext repositoryContext)
        {
            this.RepositoryContext = repositoryContext;
        }

        public IEnumerable<T> FindAll()
        {
            return this.RepositoryContext.Set<T>();
        }

        public IEnumerable<T> FindByCondition(Expression<Func<T, bool>> expression)
        {
            return this.RepositoryContext.Set<T>().Where(expression);
        }

        public void Create(T entity)
        {
            this.RepositoryContext.Set<T>().Add(entity);
        }

        public void Update(T entity)
        {
            this.RepositoryContext.Set<T>().Update(entity);
        }

        public void Delete(T entity)
        {
            this.RepositoryContext.Set<T>().Remove(entity);
        }

        public void Save()
        {
            this.RepositoryContext.SaveChanges();
        }
    }
}

This abstract class, as well as IRepositoryBase interface, use the generic type T to work with. This type T gives even more reusability to your RepositoryBase class. That means you don't have to specify the exact model (class) right now for RepositoryBase to work with, you'll do that later on.

Repository User Classes

Now that you have the RepositoryBase class, create the user classes that will inherit this abstract class. Every user class will have its own interface, for additional model specific methods. Furthermore, by inheriting from the RepositoryBase class they will have access to all methods from RepositoryBase. This way, we are separating logic, that is common for all our repository user classes, and that is specific for every user class itself.

Let's create the interfaces in the Contracts project for your Owner and Account classes.

Don't forget to add a reference from Entities project.

using Entities.Models;

namespace Contracts
{
    public interface IOwnerRepository : IRepositoryBase<Owner>
    {
    }
}
using Entities.Models;

namespace Contracts
{
    public interface IAccountRepository: IRepositoryBase<Account>
    {
    }
}

Now create repository user classes in the Repository project:

using Contracts;
using Entities;
using Entities.Models;

namespace Repository
{
    public class OwnerRepository: RepositoryBase<Owner>, IOwnerRepository
    {
        public OwnerRepository(RepositoryContext repositoryContext)
            :base(repositoryContext)
        {
        }
    }
}
using Contracts;
using Entities;
using Entities.Models;

namespace Repository
{
    public class AccountRepository: RepositoryBase<Account>, IAccountRepository
    {
        public AccountRepository(RepositoryContext repositoryContext)
            :base(repositoryContext)
        {
        }
    }
}

After these steps, finish up by creating a repository and repository user classes. But there are still more things to do.

Creating a Repository Wrapper

Imagine if inside a controller you need to collect all Owners and collect only certain Accounts (for example, domestic ones). You would need to instantiate the OwnerRepository and AccountRepository classes and then call the FindAll and FindByCondition methods.

Maybe it's not a problem when you have only two classes, but what if you need logic from 5 different classes, or even more? Having that in mind, let's create a wrapper around your repository user classes. Then place it into the IOC and, finally, inject it inside controller's constructor. Now, with that wrapper's instance, you may call any repository class you need.

Create a new interface in Contract project:

namespace Contracts
{
    public interface IRepositoryWrapper
    {
        IOwnerRepository Owner { get; set; }
        IAccountRepository Account { get; set; }
    }
}

Add a new class to the Repository project:

using Contracts;
using Entities;

namespace Repository
{
    public class RepositoryWrapper: IRepositoryWrapper
    {
        public IOwnerRepository Owner { get; set; }
        public IAccountRepository Account { get; set; }

        public RepositoryWrapper(RepositoryContext repositoryContext)
        {
            this.Owner = new OwnerRepository(repositoryContext);
            this.Account = new AccountRepository(repositoryContext);
        }
    }
}

In the ServiceExtensions class, add this code:

public static void ConfigureRepositoryWrapper(this IServiceCollection services)
{
    services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
}

And in the Startup class inside the ConfigureServices method, above the  services.AddMvc() line, add this code:

services.ConfigureRepositoryWrapper();

All you need to do is to test this code the same way you did with your custom logger in part 3 of this series.

Inject the RepositoryWrapper service inside the Values controller and call any method from the RepositoryBase class. Something like this:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private IRepositoryWrapper _repoWrapper;

    public ValuesController(IRepositoryWrapper repoWrapper)
    {
        _repoWrapper = repoWrapper;
    }
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        var domesticAccounts = _repoWrapper.Account.FindByCondition(x => x.AccountType.Equals("Domestic"));
        var owners = _repoWrapper.Owner.FindAll();

        return new string[] { "value1", "value2" };
    }
}

Place the breakpoint inside the Get() method and you'll see the data returned from the database.

Conclusion

The Repository pattern increases the level of abstraction in your code. This may make the code more difficult to understand for developers who are unfamiliar with the pattern. But once you are familiar with it, it will reduce the amount of redundant code and make the logic much easier to maintain.

In this post you have learned:

  • What the repository pattern is.
  • How to create models and model attributes.
  • How to create context class and database connections.
  • The right way to create repository logic.
  • And the way to create wrappers around your repository classes.

Thank you all for reading this post and I hope you read some useful information in it.

See you soon in the next article, where we will use repository logic to create HTTP requests.

For any suggestions or questions, don't hesitate to leave the comment in the section below.

Start coding something amazing with our library of open source Cloud code patterns. Content provided by IBM.

Topics:
web dev ,repository pattern ,c# ,.net core

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}