Implementing the Repository Pattern Using C# and Entity Developer in ASP.NET Core

Introduction

The Repository Pattern is one of the most popular design patterns used for abstracting how data is eventually persisted in the database or retrieved from the database. The basic objective of the Repository Pattern is to help you decouple the data access and business logic layers of an application. This article talks about how we can implement the repository pattern in C# and take advantage of Entity Developer to talk to the database.

Why Should We Use an ORM?

An ORM is an acronym for Object Relational Mapper - it is used to map your domain objects to the objects in the relational model. In other words, it is used to map data between incompatible type systems. Entity Developer from Devart is an ORM designer tool that lets you design your data access layer visually. It sits on top of ADO.NET and simplifies the way you work with data.

Entity Developer supports ADO.NET Entity Framework, Entity Framework Core, Hibernate, LinqConnect, Telerik Data Access, and LINQ to SQL. You can download a copy of Entity Developer and get started.

Prerequisites 

To be able to work with the code examples demonstrated in this article, you should have the following installed in your system:

You can download Visual Studio 2019 from here: https://visualstudio.microsoft.com/downloads/

You can download SQL Server 2019 Developer Edition from here: https://www.microsoft.com/en-us/sql-server/sql-server-downloads

You can download a copy of Entity Developer (trial version) from here: https://www.devart.com/entitydeveloper/download.html

Create the Database

Now that the ASP.NET Core Web API project has been created in Visual Studio 2019; the next step is to create the database. Note that for the sake of simplicity we’ll use a database with just two tables with simple design in this example.

Launch the SQL Server Management Studio and create a new database called Demo. Next, use the following script to create two tables named Products and Categories inside the Demo database.

SQL
 




xxxxxxxxxx
1
51


 
1
CREATE TABLE [dbo].[Products](
2
 
          
3
      [ID] [int] IDENTITY(1,1) NOT NULL,
4
 
          
5
      [ProductName] [nvarchar](40) NOT NULL,
6
 
          
7
      [CategoryID] [int] NOT NULL,
8
 
          
9
      [QuantityPerUnit] [nvarchar](20) NULL,
10
 
          
11
      [UnitPrice] [money] NULL,
12
 
          
13
      [UnitsInStock] [smallint] NULL,
14
 
          
15
      [ReorderLevel] [smallint] NULL,
16
 
          
17
      [Discontinued] [bit] NOT NULL,
18
 
          
19
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
20
 
          
21
(
22
 
          
23
      [ID] ASC
24
 
          
25
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
26
 
          
27
) ON [PRIMARY]
28
 
          
29
GO
30
 
          
31
 
32
 
          
33
CREATE TABLE [dbo].[Categories](
34
 
          
35
      [ID] [int] IDENTITY(1,1) NOT NULL,
36
 
          
37
      [CategoryName] [nvarchar](15) NOT NULL,
38
 
          
39
      [Description] [ntext] NULL,
40
 
          
41
 CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED 
42
 
          
43
(
44
 
          
45
      [ID] ASC
46
 
          
47
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
48
 
          
49
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
50
 
          
51
GO



Figure 1 shows how these two tables are related.

article image


We’ll use this database in the subsequent sections of this article to store, edit, delete, and retrieve data.

Create a New ASP.NET Core Web API Project

Assuming that the necessary software has been installed in your computer to be able to work with Entity Developer, follow the steps outlined below to create a new ASP.NET Core Web API project.

  1. First off, open the Visual Studio 2019 IDE
  2. Next, click "Create a new project" once the IDE has loaded
  3. Click "Create a new project"
  4. Next, select "ASP.NET Core Web Application"
  5. Click the "Next" button
  6. Specify the project name and location - where it should be stored in your system
  7. Optionally, click the "Place solution and project in the same directory" checkbox.
  8. Next, click the "Create" button
  9. In the "Create a new ASP.NET Core Web Application" dialog window that is shown next, select "API" as the project template.
  10. Select ASP.NET Core 3.1 or later as the version.
  11. You should disable the "Configure for HTTPS" and "Enable Docker Support" options by disabling the respective checkboxes.
  12. Since we'll not be using authentication in this example, specify authentication as "No Authentication".
  13. Finally, click on the "Create" button to finish the process.

Create an Entity Data Model

The next thing you should do is create an entity data model. When you create a model in Entity Developer, there are two options to choose from: Database First (this is selected by default) and Model First. In this example, we’ll take advantage of the Database First approach. Remember to select the “Generate from Database” option to generate your model from the database and select the database objects you would want to be a part of your entity data model.

Follow these steps outlined below to create an entity data model in Entity Developer using the database-first approach.

1. Select your project in the Solution Explorer Window

2. Right-click and select Add->New Item

3. Select Devart EF Core Model as the template as shown in Figure 2

article image

4. Specify a name for your entity data model and click Add

5. Specify the connection properties and test the connection

6. Click on the “Next” button to continue.

7. By default, the option “Generate from Database” will be selected. Since we want the model to be created from the database, click on the “Next” button to continue.

8. De-select all options and then specify only the database objects you would like to be a part of the model. Here’s where you should select the Products and Categories tables.

9. In the "Set up naming rules" screen you can optionally specify naming rules for your entities.

10. In the next screen, you can optionally specify the model properties.

11. In the next screen, you can optionally choose the model diagram content.

12. In the next screen, you can optionally specify code generation templates.

13. Specify Repository and Unit of Work as the code generation template as shown in Figure 3.

article image

14. Lastly, click “Finish” to complete the process.

Your ADO.NET Entity Data Model using Entity Developer will be created along with the repository and unit of work classes and interfaces. Here’s how your Entity Data Model would look like.


article image

Generated Classes and Interfaces

Here’s how the Solution Explorer Window would look like – note the generated classes and interfaces.


article image


If you observe the generated classes and interfaces in the Solution Explorer Window, you'll find three repository interfaces namely IRepository, IProductRepository, and ICategoryRepository, and three repository classes namely EntityFrameworkRepository, ProductRepository, and CategoryRepository. You'll also observe the Category and Product classes - these are model classes. You'll also observe the IUnitOfWork interface and the EntityFrameworkUnitOfWork and EntityFrameworkUnitOfWorkFactory classes - these are types used for using the unit of work design pattern. In the sections that follow we’ll examine how we can work with these classes and interfaces.

Using the Repository Classes and Interfaces

There are two approaches to building repositories in an application. These include the following: -

The easiest approach to implementing the Repository design pattern is to create a repository per business object. The generated repository we used earlier follows this approach. Since we've two tables, there are two business classes namely Product and Category, and you would typically have two classes named ProductRepository and CategoryRepository implementing two interfaces IProductRepository and ICategoryRepository respectively.

The following code snippet shows the IRepository, IProductRepository and ICategoryRepository interfaces.

Java
 




xxxxxxxxxx
1
33


 
1
public partial interface IRepository<T>
2
 
          
3
    {
4
 
          
5
        void Add(T entity);
6
 
          
7
        void Remove(T entity);
8
 
          
9
    }
10
 
          
11
 
12
 
          
13
public partial interface IProductRepository : IRepository<Product>
14
 
          
15
    {
16
 
          
17
        ICollection<Product> GetAll();
18
 
          
19
        Product GetByKey(int _ID);
20
 
          
21
    }
22
 
          
23
 
24
 
          
25
public partial interface ICategoryRepository : IRepository<Category>
26
 
          
27
    {
28
 
          
29
        ICollection<Category> GetAll();
30
 
          
31
        Category GetByKey(int _ID);
32
 
          
33
    }



The EntityFrameworkRepository class represents a generic repository and implements the IRepository interface. The ProductRepository class implements the methods declared in the IProductRepository interface and the CategoryRepository class implements the ICategoryRepository interface. Please find these classes below.

Java
 




xxxxxxxxxx
1
189


 
1
public partial class EntityFrameworkRepository<T> : IRepository<T> where T : class
2
 
          
3
 {
4
 
          
5
        private DbContext context;
6
 
          
7
        protected DbSet<T> objectSet;
8
 
          
9
 
10
 
          
11
        public EntityFrameworkRepository(DbContext context)
12
 
          
13
        {
14
 
          
15
 
16
 
          
17
            if (context == null)
18
 
          
19
            {
20
 
          
21
                throw new ArgumentNullException("context");
22
 
          
23
            }
24
 
          
25
 
26
 
          
27
            this.context = context;
28
 
          
29
            this.objectSet = context.Set<T>();
30
 
          
31
        }
32
 
          
33
 
34
 
          
35
        public virtual void Add(T entity)
36
 
          
37
        {
38
 
          
39
 
40
 
          
41
            if (entity == null)
42
 
          
43
            {
44
 
          
45
                throw new ArgumentNullException("entity");
46
 
          
47
            }
48
 
          
49
            objectSet.Add(entity);
50
 
          
51
        }
52
 
          
53
 
54
 
          
55
        public virtual void Remove(T entity)
56
 
          
57
        {
58
 
          
59
 
60
 
          
61
            if (entity == null)
62
 
          
63
            {
64
 
          
65
                throw new ArgumentNullException("entity");
66
 
          
67
            }
68
 
          
69
            objectSet.Remove(entity);
70
 
          
71
        }
72
 
          
73
 
74
 
          
75
        public DbContext Context 
76
 
          
77
        {
78
 
          
79
            get 
80
 
          
81
            {
82
 
          
83
                return context;
84
 
          
85
            }
86
 
          
87
        }
88
 
          
89
 }
90
 
          
91
 
92
 
          
93
 public partial class ProductRepository : EntityFrameworkRepository<Product>, IProductRepository
94
 
          
95
    {
96
 
          
97
        public ProductRepository(DemoModel context) : base(context)
98
 
          
99
        {
100
 
          
101
        }
102
 
          
103
 
104
 
          
105
        public virtual ICollection<Product> GetAll()
106
 
          
107
        {
108
 
          
109
            return objectSet.ToList();
110
 
          
111
        }
112
 
          
113
 
114
 
          
115
        public virtual Product GetByKey(int _ID)
116
 
          
117
        {
118
 
          
119
            return objectSet.SingleOrDefault(e => e.ID == _ID);
120
 
          
121
        }
122
 
          
123
 
124
 
          
125
        public new DemoModel Context 
126
 
          
127
        {
128
 
          
129
            get 
130
 
          
131
            {
132
 
          
133
                return (DemoModel)base.Context;
134
 
          
135
            }
136
 
          
137
        }
138
 
          
139
    }
140
 
          
141
 
142
 
          
143
public partial class CategoryRepository : EntityFrameworkRepository<Category>, ICategoryRepository
144
 
          
145
    {
146
 
          
147
        public CategoryRepository(DemoModel context) : base(context)
148
 
          
149
        {
150
 
          
151
        }
152
 
          
153
 
154
 
          
155
        public virtual ICollection<Category> GetAll()
156
 
          
157
        {
158
 
          
159
            return objectSet.ToList();
160
 
          
161
        }
162
 
          
163
 
164
 
          
165
        public virtual Category GetByKey(int _ID)
166
 
          
167
        {
168
 
          
169
            return objectSet.SingleOrDefault(e => e.ID == _ID);
170
 
          
171
        }
172
 
          
173
 
174
 
          
175
        public new DemoModel Context 
176
 
          
177
        {
178
 
          
179
            get 
180
 
          
181
            {
182
 
          
183
                return (DemoModel)base.Context;
184
 
          
185
            }
186
 
          
187
        }
188
 
          
189
    }



You can now take advantage of dependency injection to inject these instances to your controllers. The following code snippet illustrates how you can create instance of the product repository in the ConfigureServices method of the Startup class.

Java
 




xxxxxxxxxx
1
11


 
1
public void ConfigureServices(IServiceCollection services)
2
 
          
3
{
4
 
          
5
    services.AddScoped<ProductRepository>(x =>
6
 
          
7
    { return new ProductRepository(new DemoModel()); });
8
 
          
9
    services.AddControllers();
10
 
          
11
}



Use the Repository in the Controller Classes

Now that we’ve the repository class ready, let’s create controller classes and examine how we can use these repositories in them. Follow the steps given below to create the Product and Category controller classes in your project.

  1. Select the project in the SolutionExplorer Window
  2. Right-click and click on Add->New Item…
  3. Select the “API Controller with read/write actions” template
  4. Specify a name for your controller class
  5. Click Add to add the new controller to your project

To use the repository instances, we should take advantage of dependency injection to inject an instance of the ProductRepository class as shown in the code snippet given below.

Java
 




xxxxxxxxxx
1
23


 
1
public class ProductController : ControllerBase
2
 
          
3
{
4
 
          
5
   private readonly IProductRepository _productRepository;
6
 
          
7
 
8
 
          
9
   public ProductController(IProductRepository 
10
 
          
11
                             productRepository)
12
 
          
13
   {
14
 
          
15
     _productRepository = productRepository;
16
 
          
17
   }
18
 
          
19
 
20
 
          
21
  //Action methods
22
 
          
23
}



Once the repository instance is available, you can invoke its methods from the action methods in your controller classes to perform CRUD operations against the underlying database.

Use the Generic Repository

The other approach is to use a generic repository – we’ve already looked at it in the earlier section. The generated code contains a generic repository named EntityFrameworkRepository. You should first register an instance of this repository class as a scoped instance so that you can use this instance in all classes in this application.

The following code snippet illustrates how you can create an instance of the generic repository in the ConfigureServices method of the Startup class.

Java
 




xxxxxxxxxx
1
11


 
1
public void ConfigureServices(IServiceCollection services)
2
 
          
3
{
4
 
          
5
   services.AddScoped<IRepository<Product>>(x => 
6
 
          
7
      { return new EntityFrameworkRepository<Product>(new DemoModel()); });
8
 
          
9
   services.AddControllers();
10
 
          
11
}



You can now use this generic repository in your controller classes. The following code snippet shows how you can use the generic repository in the controller classes.

Java
 




xxxxxxxxxx
1
23


 
1
[Route("api/[controller]")]
2
 
          
3
[ApiController]
4
 
          
5
public class ProductController : ControllerBase
6
 
          
7
 {
8
 
          
9
    private readonly IRepository<Product> _repository;
10
 
          
11
    public DefaultController(EntityFrameworkRepository<Product> genericRepository)
12
 
          
13
        {
14
 
          
15
            _repository = genericRepository;
16
 
          
17
        }
18
 
          
19
 
20
 
          
21
     //Action methods
22
 
          
23
 }



Use the UnitOfWork Pattern

Unit Of Pattern can be used to create an abstraction layer between the data access layer and the business logic layer of an application - it helps us implement the repository pattern elegantly. The generated classes contain the IUnitOfWork interface and the EntityFrameworkUnitOfWork class. You can take advantage of these types to use UnitOfWork in your controller or business logic classes.

The following code snippet illustrates how you can take advantage of the unit of work to store a product record in the database.

Java
 




xxxxxxxxxx
1
13


 
1
using (EntityFrameworkUnitOfWork unitOfWork = new EntityFrameworkUnitOfWork(new DemoModel()))
2
 
          
3
 {
4
 
          
5
      unitOfWork.Context.Add(new Product {ID = 1, CategoryID = 3, Discontinued = false, ProductName = "ABC Laptop"
6
 
          
7
      QuantityPerUnit = 5, ReorderLevel = 20, UnitPrice = 20000.00, UnitsInStock = 100});
8
 
          
9
 
10
 
          
11
      unitOfWork.Save();
12
 
          
13
 }



Summary

The Repository Pattern was first introduced as part of Domain-Driven Design in the year 2004. Since then it has become extremely popular and has been the design pattern of choice for abstracting calls from an application to the underlying database.

Proper use of the Repository pattern improves testability, code extensibility, and maintenance. In this article, we’ve examined how we can build a repository and encapsulate all calls to the database using the Entity Developer ORM tool.

 

 

 

 

Top