创建仓库基类

数据库上下文创建完成后需要有一个专门去做数据库读写操作的地方,也就是仓库。我们把这种访问数据的方式称为仓库模式。

由于大多数模型的读写有类似的操作,因此可以将这些基本操作放到一个仓库基类中。

其他模型的仓库基础此仓库,如果模型还需要其他操作,则在各自的仓库中再添加

仓库基类

在项目根目录下创建Repositories文件夹。

Repositories文件夹下创建类文件BaseRepository.cs

我们需要在BaseRepository中使用之前创建的数据库上下文。

一种方法是直接new一个AppDbContext,如下所示:

public class BaseRepository
{
    AppDbContext context = new AppDbContext();
}

这种方法非常简单,但是不利于项目的维护。

更好的方法是将数据库上下文注册为项目中的服务,其他类通过构造函数依赖注入获取数据库上下文的实例。

将数据库上下文注册为服务

在Program.cs的Main函数中添加services.AddDbContext<AppDbContext>();

这样就能将AppDbContext注册为数据库上下文服务。

代码如下:

var builder = WebApplication.CreateBuilder(args);

IServiceCollection services = builder.Services;
services.AddControllersWithViews();
services.AddDbContext<AppDbContext>();

var app = builder.Build();
app.UseStaticFiles();
app.MapDefaultControllerRoute();

app.Run();

获取数据库上下文实例

首先创建一个AppDbContext类型的字段context

然后通过构造函数给context字段赋值。

代码如下:

public class BaseRepository
{
    AppDbContext context;

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

在这段代码中,我们并没有去new一个AppDbContext实例,而是通过构造函数告诉框架BaseRepository需要一个AppDbContext类型的实例。

而在Program.cs中,我们正好注册了一个AppDbContext类型的服务,因此当框架创建一个BaseRepository对象时,会同时通过构造函数注入AppDbContext的实例到context字段中。

主构造函数(可选)

在.NET 8中,可以通过主构造函数来简化代码

将光标一到构造函数上,按住Alt+Enter键。

选择使用主构造函数(并删除字段)

代码最终如下所示:

public class BaseRepository(AppDbContext context)
{

}

可以看到,构造函数的参数列表直接移到了类名后面,而且也不需要去声明一个AppDbContext类型的字段了,极大地简化了代码。

构建泛型

由于BaseRepository是一个基类,此时不会指定操作哪一个模型,因此需要添加泛型。

添加泛型的方式是在类名后添加<T>,如下所示:

public class BaseRepository<T>(AppDbContext context)
{

}

之后在具体的仓库类中可以将T替换为具体的模型类,例如Major、Student等。

添加限定

泛型需要是一个类,并且还需要有一个不含参数的构造函数,因此还需要给泛型加限定条件。

代码如下:

public class BaseRepository<T>(AppDbContext context) where T : class, new()
{

}

其中where T : class表示T是一个类,后面的new()表示T存在一个无参构造函数。

获取数据表

通过context.Set<T>(),可以获取T类型的数据表的访问。

然后将数据表赋值给DbSet属性,代码如下:

public class BaseRepository<T>(AppDbContext context) where T : class, new()
{
    protected DbSet<T> DbSet => context.Set<T>();
}

插入数据

DbSet可以通过Add方法实现数据的添加操作。

代码如下:

public void Insert(T entity)
{
    DbSet.Add(entity);
    context.SaveChanges();
}

Add方法仅仅只是将数据保存在内存中,要存储到数据库中还需要执行contextSaveChanges()方法。

删除数据

通过Remove方法可以实现数据的删除操作。

代码如下:

public void Delete(T entity)
{
    DbSet.Remove(entity);
    context.SaveChanges();
}

更新操作

通过Update方法可以实现数据的删除操作。

代码如下:

public void Update(T entity)
{
    DbSet.Update(entity);
    context.SaveChanges();
}

查找单个数据

DbSetFind方法可以通过主键查找一个具体的数据,如果找到,则返回实体,否则返回null

代码如下:

public T? Get(Guid id)
{
    return DbSet.Find(id);
}

其中,参数中的Guid id是模型的主键,T?表示可空类型。

获取数据列表

DbSet通过ToList()方法可直接返回模型的列表。

代码如下:

public List<T> GetList()
{
    return DbSet.ToList();
}

代码总览

最终完整的代码如下:

public class BaseRepository<T>(AppDbContext context) where T : class, new()
{
    protected DbSet<T> DbSet => context.Set<T>();

    public void Insert(T entity)
    {
        DbSet.Add(entity);
        context.SaveChanges();
    }

    public void Delete(T entity)
    {
        DbSet.Remove(entity);
        context.SaveChanges();
    }

    public void Update(T entity)
    {
        DbSet.Update(entity);
        context.SaveChanges();
    }

    public T? Get(Guid id)
    {
        return DbSet.Find(id);
    }

    public List<T> GetList()
    {
        return DbSet.ToList();
    }
}