ASP.NET Core 中的仓储模式

Author Image
admin Thursday, June 3, 2021 阅读数: 88
Share:

本文作者:梁桐铭- 微软最有价值专家(Microsoft MVP)
本文出自《从零开始学 ASP.NET Core 与 EntityFramework Core》目录
视频课程效果更佳:跨平台开发实战掌握 ASP.NET Core 与 EntityFramework Core

ASP.NET Core 中的仓储模式

在这个章节中,我们将讨论

  • 什么是仓储模式
  • 仓储模式的好处
  • 在 Entity Framework Core 中使用仓储模式从 SQLServer 数据库存储和查询数据的示例

备注:Repository 可以理解为专有门词,在本书中代表为 仓储,在其他书籍的翻译中有存储库的意思。

什么是仓储模式

仓储服务是数据访问层的抽象呈现。它隐藏了如何从底层数据源保存或查询数据的详细信息。有关如何存储和查询数据的详细信息,有关如何保存和查询数据的详细信息都在对应的仓储中。 例如,

  • 您可能有一个仓储服务,用于存储和查询内存中的数据。
  • 您可能拥有另一个仓储服务,该仓储服务用于存储和查询SQL Server等数据库中的数据。
  • 还有另一个仓储服务,用于存储和查询XML或CSV文件中的数据。
  public class Student
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public MajorEnum? Major { get; set; }

        public string Email { get; set; }
    }

使用仓储模式,我们可以建立基于Student类的仓储,可以使它完成基于学生信息的增、删、改、查操作,而无需去写大量的代码。

仓储模式中的接口

我们使用接口来指定的仓储模式,比如:

  • 仓储支持哪些操作(即具体实现的方法)。
  • 每个操作所需的数据,即需要传递给方法的参数和该方法返回的数据信息。
  • 仓储接口包含它可以执行的操作,但不实现它如何操作,只实现它可以执行的操作
  • 而具体实现的细节则位于实现仓储接口的对应仓储类中

IStudentRepository 接口支持以下操作

public  interface IStudentRepository
    {
        Student GetStudent(int id);
        IEnumerable<Student> GetAllStudents();
        Student Add(Student student);
        Student Update(Student updateStudent);
        Student Delete(int id);
    }


我们当前项目中有一个学生仓储接口名为IStudentRepository,它下面有几个方法:

  • 获取所有的学生信息
  • 通过 Id 来获取学生信息
  • 添加一名新的学生信息
  • 更新一名学生的信息
  • 删除一名学生的信息

而这些方法具体实现的细节,则是在继承自IStudentRepository仓储接口中的仓储类中。

仓储模式--内存实现

以下 MockStudentRepository 类提供了一个实现 IStudentRepository 。 该特定实现从内存中的集合中进行学生信息的查询和保存。

 public class MockStudentRepository : IStudentRepository
    {
        private List<Student> _studentList;


        public MockStudentRepository()
        {

            _studentList = new List<Student>()
            {
            new Student() { Id = 1, Name = "张三", Major = MajorEnum.FirstGrade, Email = "Tony-zhang@52abp.com" },
            new Student() { Id = 2, Name = "李四", Major = MajorEnum.SecondGrade, Email = "lisi@52abp.com" },
            new Student() { Id = 3, Name = "王二麻子", Major = MajorEnum.GradeThree, Email = "wang@52abp.com" },
            };

        }

        public Student Add(Student student)
        {
            student.Id = _studentList.Max(s => s.Id) + 1;
            _studentList.Add(student);
            return student;
        }

        public IEnumerable<Student> GetAllStudents()
        {
            return _studentList;

        }
        public Student GetStudent(int id)
        {
            return    _studentList.FirstOrDefault(a => a.Id == id);
        }
        public Student Delete(int id)
        {
          Student student=  _studentList.FirstOrDefault(s => s.Id == id);

            if (student!=null)
            {
                _studentList.Remove(student);

            }
            return student;

        }
        public Student Update(Student updateStudent)
        {
            Student student = _studentList.FirstOrDefault(s => s.Id == updateStudent.Id);

            if (student != null)
            {
                student.Name = updateStudent.Name;
                student.Email = updateStudent.Email;
                student.Major = updateStudent.Major;
            }
            return student;
        }
    }

Repository 模式- SQLServer 数据库实现

以下代码是 SQLStudentRepository 类提供了IStudentRepository的另一种实现 。此特定实现使用 EF CORE 从 SQLServer 数据库中进行学生信息的查询和保存。

 public class SQLStudentRepository : IStudentRepository
    {
        private readonly AppDbContext context;

        public SQLStudentRepository(AppDbContext context)
        {
            this.context = context;
        }

        public Student Add(Student student)
        {
            context.Students.Add(student);
            context.SaveChanges();
            return student;

        }

        public Student Delete(int id)
        {
            Student student = context.Students.Find(id);

            if (student!=null)
            {
                context.Students.Remove(student);
                context.SaveChanges();
            }

            return student;

        }

        public IEnumerable<Student> GetAllStudents()
        {
            return context.Students;
        }

        public Student GetStudent(int id)
        {
            return context.Students.Find(id);
        }

        public Student Update(Student updateStudent)
        {
            var student = context.Students.Attach(updateStudent);
            student.State = Microsoft.EntityFrameworkCore.EntityState.Modified;
            context.SaveChanges();
            return updateStudent;
        }
    }

选择合适的仓储实现模式

在我们的应用程序中我们可以在HomeController中查看以下StudentRepository 的实现。因为我们已经通过 ASP.NET Core 中的依赖注入组件注册了IStudentRepository 的实例。

StudentRepository 接口有 2 个实现分别是,SQLStudentRepositoryMockStudentRepository

那么会带来一个问题,应用程序如何知道具体要使用哪个仓储的实现呢?

答案是在 Startup.cs 文件的 Startup 类中。

它可以让 ASP.NET Core 在请求 IStudentRepository 实例时提供 SQLStudentRepository 类的实例。而我们会选择使用 AddScoped() 方法,是因为我们希望实例处于活动状态并且可以包含当前 HTTP 请求的整个生命周期中。而对于另一个新的 HTTP 请求,将提供一个新的 SQLStudentRepository 类实例,它将在该 HTTP 请求的整个范围周期内是可用的。

现在我们的整个应用程序中,在注入 IStudentRepository 的所有位置都提供了 SQLStudentRepository 的实例。如果您希望应用程序使用其他的实现,只需要更改的是以下一行代码。

public void ConfigureServices(IServiceCollection services)
{
   //其他代码
 services.AddScoped<IStudentRepository, SQLStudentRepository>();
}

而我们在之前的章节有探讨过 AddSingleton() ,AddScoped()和 AddTransient() 三者的不同,你可以回到 AddSingleton vs AddScoped vs AddTransient 三者的差异性中查看详细信息;

使用仓储模式的好处

  • 代码更清晰,更易于重用和维护。
  • 它使我们能够创建松散耦合的系统。例如,如果我们希望我们的应用程序与 oracle 数据库工作而不是使用 SQLServer 工作,那么我们只需要实现一个OracleRepository,再使用依赖注入系统来注册 OracleRepository,那么就可以轻松实现一个基于 Oracle 数据库的读取和保存的仓储服务。
  • 在单元测试项目中,很容易用模拟的实现来替换真实的仓储以进行测试。
  • 仓储模式不是特定说服务于数据库模式,而是我们实际开发中,经常与数据库打交道而已,所以经常使用数据库仓储模式,除此以外在一些公司里面进行大量单元测试的时候也会用到内存仓储模式。

文章说明

如果您觉得我的文章质量还不错,欢迎打赏,也可以订阅我的视频哦
未得到授权不得擅自转载本文内容,52abp.com 保留版权
感谢您对我的支持

关注微信公众号:角落的白板报

公众号:角落的白板报