通用数据访问层设计

在软件开发中,数据访问层(Data Access Layer, DAL)是一个重要的组成部分,它负责与数据库进行交互。为了提高代码的可维护性和可测试性,通常会采用一种设计模式来封装数据访问逻辑,这就是所谓的“仓库模式”(Repository Pattern)。仓库模式的目的是隐藏数据访问的细节,使得应用程序的其他部分可以轻松地查询数据对象,而无需了解如何提供诸如连接字符串之类的细节。此外,仓库模式还在应用程序的数据和领域层之间增加了一个抽象层,使得数据访问部分的代码更加容易进行测试。

仓库接口的初步设计

首先,定义了一个仓库接口,它包含了针对特定实体类(如Employee类)的CRUD(创建、读取、更新、删除)操作。

public interface IEmployeeRepository { Task Get(Guid? id); Task Save(Employee employee); Task Delete(Employee employee); Task Update(Employee employee); Task> FindAll(); }

这个接口是针对Employee类设计的,包含了基本的CRUD操作。接下来,实现了一个EmployeeRepository类,它使用DbContext来与数据库交互。

public class EmployeeRepository : IEmployeeRepository { private EmployeeContext _employeeContext; public EmployeeRepository() { _employeeContext = new EmployeeContext(); } public async Task Get(Guid? id) { return await _employeeContext.Employees.FirstOrDefaultAsync(x => x.Id == id); } public async Task Save(Employee employee) { _employeeContext.Employees.Add(employee); await _employeeContext.SaveChangesAsync(); } public async Task Delete(Employee employee) { _employeeContext.Employees.Remove(employee); await _employeeContext.SaveChangesAsync(); } public async Task Update(Employee employee) { _employeeContext.Employees.Update(employee); await _employeeContext.SaveChangesAsync(); } public async Task> FindAll() { return await _employeeContext.Employees.ToListAsync(); } }

EmployeeContext类继承自DbContext,并定义了一个DbSet类型的Employees属性,用于表示数据库中的员工集合。

public class EmployeeContext : DbContext { private static bool _created = false; public EmployeeContext() { if (!_created) { Database.EnsureCreated(); _created = true; } } public DbSet Employees { get; set; } protected override void OnConfiguring(EntityOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryStore(); } }

然而,EmployeeRepository的实现存在两个问题:首先,它只使用了Employee这一个模型类,如果有多个模型类,就需要复制大量的代码;其次,它不容易进行测试。为了解决这些问题,可以将仓库接口泛化,并注入上下文对象。

泛化仓库接口IGenericRepository允许为任何实现了IEntity接口的实体类创建仓库。IEntity接口包含一个Id属性。

public interface IGenericRepository where T : class, IEntity, new() { Task Get(Guid? id); Task Save(T entity); Task Delete(T entity); Task Update(T entity); Task> FindAll(); }

IEntity接口定义如下:

public interface IEntity { Guid Id { get; set; } }

接下来,实现了GenericRepository类:

public class GenericRepository : IGenericRepository where T : class, IEntity, new() { private DbContext _dbContext; public GenericRepository(DbContext dbContext) { _dbContext = dbContext; } public async Task Delete(T entity) { _dbContext.Set().Remove(entity); await _dbContext.SaveChangesAsync(); } public async Task> FindAll() { return await _dbContext.Set().ToListAsync(); } public async Task Get(Guid? id) { return await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == id); } public async Task Save(T entity) { _dbContext.Set().Add(entity); await _dbContext.SaveChangesAsync(); } public async Task Update(T entity) { _dbContext.Set().Update(entity); await _dbContext.SaveChangesAsync(); } }

在GenericDbContext类中,使用反射来动态地为DbContext添加所有实现了IEntity接口的类型。

public class GenericDbContext : DbContext { private static bool _created = false; public GenericDbContext() { if (!_created) { Database.EnsureCreated(); _created = true; } } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryDatabase(true); } protected override void OnModelCreating(ModelBuilder modelBuilder) { var types = Assembly.GetExecutingAssembly().GetTypes() .Where(type => typeof(IEntity).IsAssignableFrom(type) && type.IsClass); var method = typeof(ModelBuilder).GetMethods().First(m => m.Name == "Entity" && m.IsGenericMethodDefinition && m.GetParameters().Length == 0); foreach (var type in types) { method = method.MakeGenericMethod(type); method.Invoke(modelBuilder, null); } base.OnModelCreating(modelBuilder); } }

在ASP.NET 5中,可以使用内置的依赖注入功能来注入仓库和上下文。

public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddScoped(); services.AddScoped, GenericRepository>(); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485