A typical software application will invariably need to access some kind of data store in order to carry typical CRUD (Create, Read Update, Delete) operations on data.

Typically, this could be some kind of database, file system or any kind of storage mechanism used to store data.

In some cases saving or creating data may actually require persistence to new files or creating a new row in a data table, In other cases saving new data it may actually require a number of interactions with several web-based services.

 

In this post, we will explore the Repository and Unit Of Work pattern, beyond using it to provide an abstraction over the Entity Framework, in order to gain a better understanding of the two patterns and we’ll explore how to use the patterns in order to provide a data layer abstraction in software applications.

In most modern software applications it is becoming increasingly common for the application to have several different data stores from different vendors or even different application Apis. For instance, you may be accessing data from an Oracle HR database, accounting data may be in a SQL server, customer data may be in mySQL based CRM and Product data may be stored in a Cloud Hosted Inventory Management Database.

Your application may be required to perform modifications to all these systems during a course of a transaction. In most cases, you may also have several different data access strategies such as any number of different Object Relational Mappers (ORM) to interact with the various databases.

The challenge for developers in these types of scenarios, is often they don’t want to expose the varying different data access logic to their UI layer i.e. Controller or even Business Logic Layers often with the aim of abstracting it in order to reduce direct dependencies.

It is in cases like this that the Repository Pattern comes in extremely useful.

The repository pattern is discussed extensively in the following resources and Patterns of Enterprise Application Architecture and Domain Driven Design

The two books are an essential component of any developers reference materials. I feel the two books work well together in providing Enterprise software developers a thorough grounding on the importance of software patterns and how and when to consider and use them when designing and developing software applications.

Patterns of Enterprise Application Architecture is basically two books in one. The first is a short tutorial on developing enterprise applications, which you can read from start to finish to understand the scope of the book’s lessons.

The second part of the book provides a detailed reference to software design patterns themselves. Each pattern provides usage and implementation information, as well as detailed code examples in Java or C#. The entire book is also richly illustrated with UML diagrams to further explain the concepts.

Domain-Driven Design provides a systematic approach to domain-driven design, presenting an extensive set of design best practices, experience-based techniques, and fundamental principles that facilitate the development of software projects facing complex domains.

What is the Repository Pattern

Software developers use the repository pattern to separate the logic that retrieves the data and maps it to an entity model from the business logic that acts on the model. This enables the business logic to be agnostic to the type of data that comprises the data source layer.

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.

MARTIN FOWLER

I’ve been using a Generic Repository Pattern in some form or fashion for a number of years. The Repository Pattern is an approach to abstract away the details of the data access layer from the rest of the application. Using a Generic Repository is much easier to keep from business logic creeping in where it doesn’t belong!

The repository acts as a mediator between the data source layer and the business layers of the application. It queries the data source for the data, maps the data from the data source to a business entity, and persists changes in the business entity to the data source.

 

Information

The Repository Pattern separates the business logic from interactions with an underlying data source

Benefits of the Repository Pattern

  • Centralization of the data access logic.
  • Substitution point for the unit tests.
  • Flexible architecture that can be adapted as the overall design of the application evolves.

In order to enable your application to meet all the illities i.e.

  • Scalability
  • Adaptability
  • Testability
  • Usability
  • Maintainability
  • Compatability
  • Reliability
  • Extensibility
  • Portability
  • Interoperability
  • Reusability

It’s important to break up the development of your application into layers.  Each layer can then be injected.

This provides levels of abstractions for your various layers in that they do not necessarily explicitly care where the data from each layer is persisted and retrieved from only that it conforms to an explicit data contract.

This also enables ease of testing by providing the ability to inject Mock or Fake abstracted classes to provide data.

Unit Of Work

When implementing a Repository pattern it is also important to understand the Unit of Work pattern. Fowler provides an explanation of the Unit Of Work pattern

A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you’re done, it figures out everything that needs to be done to alter the database as a result of your work.

Martin Fowler

The unit of work represents a transaction when used in data layers. Typically the unit of work will roll back the transaction if Commit() has not been invoked before being disposed.

How to implement the repository pattern

 

In this example, we are going to implement a very simple repository pattern based as an abstraction over Entity Framework core, primarily because historically this has been the most popular use of the Repository pattern.

This serves to highlight the constituent parts of the repository pattern. We’ll also establish whether the pattern is still actually suited to this kind of design problem.

The repository pattern is really easy to implement, but it is also incredibly easy to over complicate, confuse and misimplement.

When starting out with the repository pattern, it is best to stick to the principles of YAGNI (You ain’t Gonna Need It), so always try to keep your repository simple and clean.

Should you use the Repository Pattern to Wrap Entity Framework?

This is a valid question!

It is also a question that many developers are debating:

 

I’ve read the posts and I find myself agreeing with them. Everything they say, is indeed valid in certain contexts, but then again in others they may be wrong.

What it all comes down too, is context. It’s what is known as in the trade as the Golden Hammer, or the over-reliance on a familiar tool.

if all you have is a hammer, then everything looks like a nail.

I’d be the first to admit, that the repository pattern is not the best solution for every data access strategy, but it can still be the best solution in certain contexts.

Learning how and when to use any software pattern, is still far more important than just learning a pattern.

The context most of the above links mainly focus on, is using the Repository pattern as a mechanism to abstract the use of Entity Framework within your application. This definitely not the most optimum use for the repository pattern.

A few years ago, when the first release of Entity Framework came out – circa 2008 – 09 – it did not lend itself that well to writing unit tests and developers mostly found working with it to be a frustrating experience.

It was around this time, that it became rather fashionable to abstract the use of Entity Framework behind the Repository Pattern and Unit Of Work pattern.

Over the years Entity Framework and in particular Entity Framework Core, like all software has improved and now is easier to use and there is now no need to abstract it in order to write unit tests. In fact, as you’ll see in Entity Framework Core In Memory Testing database it’s now even possible to use EF Core to write better unit tests!

Taking a high-level view of an ORM, such as Entity Framework (EF), one might conclude that it is nothing more than an implementation of the Repository Pattern and a Unit of Work Pattern, and you would be right, It is, and it does provide an abstraction for interacting with the database.

EF Core almost eliminates the need for developers to write SQL queries directly in their code and providing the ability to design and manage the database.

DbContext, within Entity Framework is an example of the Unit Of Work and, IDbSet<T> is a repository providing an abstraction layer over the data access layer.

It is a good idea to take time and review the Entity Framework Core source code and in particular the code for DbContext to see how it all works together

Undoubtedly there will be POCO classes define that reflect the Database entities etc. However, in many cases, these may not always resemble the Business Model of the entities. You may have Domain Entity models which, may actually comprise several Database Entities into one Model.

The issue here is that you end up exposing your Database Object Model to your Middle and even User Interface, which can lead to some crazy misunderstanding and confusion.

Obviously, this is also dependent on the size and purpose of your application, but in most large-scale applications you may want to provide another level of abstraction to make it easier to manage your data layer.

Generally, it is a good idea to expose your Repository layer to a Service layer, which then provides domain entity objects to the UI & Business Layer.

Another use case may be, that EF actually has a lot of functionality that you may not want to be exposed to your application. So developing a Repository abstraction layer may provide a mechanism for controlling what functionality is available.

For instance, in the example, we may want to exclude Asynchronous Functionality to our database. Which is still a valid use case. So we will create a repository abstraction layer that only provides synchronous methods to interact with data, so developers are not tempted to use the asynchronous functionality.

I would emphasis, that it is not always necessary to use the Repository pattern within your application in order to abstract Entity Framework, I would caution that this type of approach has become known as an anti-pattern.

An anti-pattern is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive.

Repository Interface

We will start off developing our Repository by defining an interface it is going to adhere too.

The interface defined here is a simple one with just a handful of methods in order to sufficiently describe and implement the repository pattern.

Warning

I would caution the reader that, this is not a full implementation, but rather an example. It serves to highlight functions that are implemented by invoking the repository pattern.

C#
 
 
 
 
 
 
public interface IRepository<T> where T : class
    {
        IEnumerable<T> Get();
        IEnumerable<T> Get(Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Update(T entity);
      }
 

We will also define a Simple Unit Of Work interface.

C#
 
 
 
 
 
 
public interface IUnitOfWork : IDisposable
    {
        DbContext Context { get;  }
        void Commit();
    }
 

The code above is a typical implementation I’ve seen how some developers will implement a Unit Of Work is just enough to get it working.

C#
 
 
 
 
 
 
public class UnitOfWork :  IUnitOfWork
    {
        public DbContext Context { get; }

        public UnitOfWork(DbContext context)
        {
            Context = context;
        }
        public void Commit()
        {
            Context.SaveChanges();
        }

        public void Dispose()
        {
           Context.Dispose();
            
        }
    }
 

Repository

We can simply define our base repository class add the logic to the method it will need to perform. I have kept the code very simplistic and implemented just enough code to get it working for an example point of view.

This is just an example to illustrate how the Unit of Work and the Repository Pattern work together.

The Unit of Work is injected into the repository, as a mechanism to share whichever context it is working with. The job of actually calling the Commit method to save the changes to the database is the responsibility of the calling application and is not in the repository.

C#
 
 
 
 
 
 
public class Repository<T> : IRepository<T> where T : class
    {
       private readonly IUnitOfWork _unitOfWork;
        public Repository(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }
        public void Add(T entity)
        {
            _unitOfWork.Context.Set<T>().Add(entity);
        }

        public void Delete(T entity)
        {
            T existing = _unitOfWork.Context.Set<T>().Find(entity);
            if (existing != null) _unitOfWork.Context.Set<T>().Remove(existing);
        }

        public IEnumerable<T> Get()
        {
            return _unitOfWork.Context.Set<T>().AsEnumerable<T>();
        }

        public IEnumerable<T> Get(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
        {
            return _unitOfWork.Context.Set<T>().Where(predicate).AsEnumerable<T>();
        }

        public void Update(T entity)
        {
            _unitOfWork.Context.Entry(entity).State = EntityState.Modified;
            _unitOfWork.Context.Set<T>().Attach(entity);
        }
    }
 

You will notice from the example above, we have injected the IUnitOfWork into the Repository class. You may also notice that there aren’t any transactions implemented within the repository class, that’s because transactions need to be implemented at a higher level because a transaction may contain several operations across different repositories.

The Unit of Work is the area of code in which you may attempt to set the scope that the same IUnitOfWork instance will be used everywhere within a single request. Having a single Unit or Work per request is necessary for the pattern to function correctly.

In theory, you should inject your Unit of work into your repository classes. However, in practice, you may need to deviate.

The above implementation of the repository pattern, is typically a very simplified common implementation of what I have seen how the two patterns have been implemented. It is also primarily the reason why so many of the nasty bugs people have reported occurred!

It is also Cumbersome in a sense because it actually required developers to inject both the IRepository and IUnitOfWork Interface into their application. Which gave the impression that the two patterns operate separately.

An example of the code implementation of a class using the above would look something like this

C#
 
 
 
 
 
 
public class SomeService
{
    private readonly IUnitOfWork _uow;
    private readonly IRepository<SomeClass> _repo;
    
     public SomeService(IUnitOrWork unit, IRepository<SomeClass> repo)
    {
        _uow = unit;
        _repo = repo;
    }
    
    public void SomeMethod(SomeClass entity)
    {
        _repo.Add(entity);
        _uow.Commit();
        
    }
}
 

An architectural representation of this implementation would look something similar to this

You may be able to discern from the diagram, that this is not the optimal solution and what actually happened here is that we have the potential to introduce bugs into our code due in part that the Repository and the Unit Of Work are two separate objects, which may lead developers to think they are used separately!

In this kind of situation, you’re opening yourself to a field of hurt!

Avoid

Don’t do this or else you’ll have some crusty Carmudgeon on your back!

Improved Implementation

A better approach and essentially the approach as to how Entity Framework takes is to expose the repositories to your application via the Unit Of Work.

The simplified implementation of this looks something similar to this

C#
 
 
 
 
 
 
 public interface IUnitOfWork : IDisposable
    {
        IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
        
        int Commit();
    }
    public interface IUnitOfWork<TContext> : IUnitOfWork where TContext : DbContext
    {
        TContext Context { get; }
    }
 

The slighty modified repository interface now looks something like this and you’ll notice a few more methods have been added to help with some finer grained circumstances.

C#
 
 
 
 
 
 
public interface IRepository<T> : IDisposable where T : class
    {
         IQueryable<T> Query(string sql, params object[] parameters);
        
        T Search(params object[] keyValues);
        
        T Single(Expression<Func<T, bool>> predicate = null,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null,
            bool disableTracking = true);
        
        void Add(T entity);
        void Add(params T[] entities);
        void Add(IEnumerable<T> entities);
        void Delete(T entity);
        void Delete(object id);
        void Delete(params T[] entities);
        void Delete(IEnumerable<T> entities);
        
        
        void Update(T entity);
        void Update(params T[] entities);
        void Update(IEnumerable<T> entities);
    }
 

This now this doesn’t lead developers down the path of injecting two seemingly unconnected instances into the application and they only need to inject one unit of work to gain access to the repositories.

C#
 
 
 
 
 
 
public class SomeService
{
    private readonly IUnitOfWork _uow;
      
    public SomeService(IUnitOrWork unit )
    {
        _uow = unit;
    }
    
    public void SomeMethod(SomeClass entity)
    {
        _uow.GetRepository<SomeClass>().Add(entity);
       _uow.Commit();
        
    }
}
 

The eagle eyed among you will notice that this is similar to how entity framework DbContext works

C#
 
 
 
 
 
 
context.Add<Entity>(entity);
context.SaveChanges();
 

Architecturally, we’ll notice that this has cleaned things up a bit and we can get a clearer picture of how all th parts work together.

Abstracting the use of Entity Framework

As discussed previously, the most common use of the Repository and Unit Of Work Pattern, is to abstract the use of Entity Framework and not to inject the DbContext into the ASP.net MVC Contollers with Primary Concern that “What if we want to Change ORM’s in the future” which is Conventional Enterprise Architecture Thinking.

There are a couple issues with this thinking

  • The use case for Changing or Swapping ORM’s hardly ever comes to fruition!
  • There are better patterns to use to mitigate that risk

Entity Framework core is great, but personally I caution against injecting the EF Core context direct into your Controller and often this will have nothing do with wanting to swap ORM’s in the future.

The primary reason for this to keep your controller methods clean, small and primarily focused on Presenting data your UI layer.

A typical example of this, is that your Database Entities will often be different to your UI presentation models. Often there is a requirement to Map between the two Objects. Often developers will implement Automapper to simplify the Mapping process and I have previously discussed my approach to using AutoMapper in Domain Objects with DotNet Core

The pattern which is more ideally suited for such a use case is known as the Service Layer Pattern

A Service Layer defines an application’s boundary and its set of available operations from the perspective of interfacing client layers. It encapsulates the application’s business logic, controlling transactions and coor-dinating responses in the implementation of its operations.

Many developers confuse the Service Layer Pattern with the Repository Pattern, the primary reason why is that the Service Layer will usually provide an abstraction for the Repository & Unit Of Work pattern in order to de-couple the presentation layer from the Repository.

Why Entity Framework Core is an Implementation of the Unit Of Work and Repository Pattern

The previous implementations of Entity Framework, prior to version 6 and Core, were not exactly a full implementation of the Repository and Unit of Work pattern. All software goes through evolutionary process.

Some design ideas work others don’t. Entity Framework has had a fair few of the ones that don’t! Which you have to remember, Microsoft or any large software vendor will find out when they release software to vast customer bases, customers will try and do use your products in use cases they had not previously envisioned. It’s not that the developers or engineers themselves at poor, its just that customers have alternate thinking.

All software is prone to being over-hyped and expectations are often too high. We have an adage in the industry of “Wait for version 2”, in the case of Entity Framework, in my opinion this most definitely meant “Wait for version 6”.

Although for the most part, Version 4 was a considerable improvement. It still had a number of little gotchas and quirks. However, the release of version 4, for the most part, negated the use of implementing the Repository and Unit of Work patterns over Entity Framework.

These are the situations and context to which links, defined above, relate to. Primarily the use cases where developers were using the Repository and Unit of Work patterns to abstract the use of Entity Framework because this was seen as a mechanism to be able to switch ORM’s at a later stage.

I would argue, that Ultimately, this is completely the wrong pattern for the use case, but that is another subject and post entirely.

Conclusion

The Repository pattern is intended to create an abstraction layer between Data Access layer and business layer so it can help to insulate the application from changes in the data store and facilitate automated unit testing for test-driven development.

Entity Framework Core in an implementation of the Unit Of Work and Repository Pattern. Therefore it is not entirely necessary these days to wrap Entity Framework Core in a Unit Of Work/Repository pattern, because fundamentally you’re just wrapping an abstraction over an abstraction.

关于作者:

 

Gary Woodfine

Technical Director at Denizon
Gary is Technical Director at Denizon, an independent software vendor specialising in IoT, Field Service and associated managed services,enabling customers to be efficient, productive, secure and scalable in a way which helps them address and reduce their ecological impact.

Denizon's product line successfully integrate IoT, Artificial Intelligence and Blockchain technology to enable efficient, productive, secure and scalable solutions to help organisations address increasing energy demands, ecological impact and Health & Safety concerns of their staff.

Using the Repository and Unit Of Work Pattern in .net core的更多相关文章

  1. TinyFrame升级之七:重构Repository和Unit Of Work

    首先,重构的想法来源于以下文章:Correct use of Repository and Unit Of Work patterns in ASP.NET MVC,因为我发现在我的框架中,对Unit ...

  2. Follow me to learn what is Unit of Work pattern

    Introduction A Unit of Work is a combination of several actions that will be grouped into a transact ...

  3. 在Entity Framework 4.0中使用 Repository 和 Unit of Work 模式

    [原文地址]Using Repository and Unit of Work patterns with Entity Framework 4.0 [原文发表日期] 16 June 09 04:08 ...

  4. 转载Repository 和Unit of work的使用说明

    利用Repository and Unit of Work重构项目 文章索引和简介 项目最基础的东西已经结束了,但是现在我们的项目还不健全  不利于测试 重复性代码多   层与层之间耦合性高  不利于 ...

  5. MVC3+EF4.1学习系列(八)-----利用Repository and Unit of Work重构项目

    项目最基础的东西已经结束了,但是现在我们的项目还不健全  不利于测试 重复性代码多   层与层之间耦合性高  不利于扩展等问题.今天的这章 主要就是解决这些问题的.再解决这些问题时,自己也产生了很多疑 ...

  6. MVC+EF 理解和实现仓储模式和工作单元模式

    MVC+EF 理解和实现仓储模式和工作单元模式 原文:Understanding Repository and Unit of Work Pattern and Implementing Generi ...

  7. Using the Repository Pattern with ASP.NET MVC and Entity Framework

    原文:http://www.codeguru.com/csharp/.net/net_asp/mvc/using-the-repository-pattern-with-asp.net-mvc-and ...

  8. [转]Using the Repository Pattern with ASP.NET MVC and Entity Framework

    本文转自:http://www.codeguru.com/csharp/.net/net_asp/mvc/using-the-repository-pattern-with-asp.net-mvc-a ...

  9. EF6 CodeFirst+Repository+Ninject+MVC4+EasyUI实践(完)

    前言 这一篇是本系列的最后一篇,虽然示例讲到这里就停止呢,但对于这些技术的学习远不能停止.虽然本示例讲的比较基础,但是正如我第一篇说到的,这个系列的目的不是说一些高端的架构设计,而是作为一个入门级,对 ...

随机推荐

  1. Java学习:List接口

    List接口 java.util.list接口 extends Collection接口 List接口的特点: 有序的集合,存储元素和取出元素的顺序是一致的(存储123 取出123) 有索引,包含了一 ...

  2. 对ssm框架里面的一些常用注解的理解

    @Componcnt :作用就是把当前类对象存入spring容器中 属性:value 用于指定bean的id 当我们不写的时候默认就是当前类名,并且首字母要小写 ------------------- ...

  3. 如何在JIRA中有效使用关注和@提及 我正在关注的问题 提及我的问题 在仪表板上显示

    如何在JIRA中有效使用关注和@提及http://bbs.51testing.com/forum.php?mod=viewthread&tid=1157043&fromuid=1530 ...

  4. python numPy模块 与numpy里的数据类型、数据类型对象dtype

    学习链接:http://www.runoob.com/numpy/numpy-tutorial.html 官方链接:https://numpy.org/devdocs/user/quickstart. ...

  5. Java自学-数字与字符串 比较字符串

    Java 比较字符串 示例 1 : 是否是同一个对象 str1和str2的内容一定是一样的! 但是,并不是同一个字符串对象 package character; public class TestSt ...

  6. 如何快速找到Chrome配置文件路径,MAC 与window 都适用

    Chrome 的配置文件主要用于存储浏览器的相关配置.书签.扩展插件和密码等,Chrome 配置文件会存储在用户计算机的一个单独文件夹当中,当你升级或重装浏览器时,这些已有配置将可以被完整保存下来. ...

  7. Beego 学习笔记一:环境的配置

    Beego 环境的配置 1>     下载go,并安装.下载地址是: https://golang.org/dl/.最好选择这部分的最新的下载安装 2>     配置环境变量(若是没有配置 ...

  8. 2007英语CET6四6级资料六级大学单词

    anticipation n. 预期,期望 appreciation n. 感谢,感激 array n. 陈列,一系列 assurance n. 保证 emergency n. 紧急情况 encour ...

  9. Api测试-为postman自动添加cookie

    使用postman来调试接口,会被buc-sso-csrf等拦截,需要自己挨个添加cookie,但是cookie又有失效时间,所以本篇介绍如何使用插件来自动获取cookie进行接口api测试 一.安装 ...

  10. Junit测试。

    Junit使用: 白盒测试 步骤: 1.定义测试类. 2.定义测试方法:可以单独运行. 3.给方法加@Test,导入junit依赖环境. 判定结果: 红色:失败  绿色:成功. 一般不看输出,而是使用 ...