EFCore集成

EFCore框架相关的内容可以参考笔记库的软件工程/dotNet/EFCore相关章节。这篇笔记我们介绍如何在ASP.NET Core工程中使用EFCore操作数据库表。

安装EFCore依赖

我们这里使用.NET6版本和MySQL数据库,执行命令安装以下NuGet依赖项。

dotnet add package Microsoft.EntityFrameworkCore --version 6.0.36
dotnet add package Microsoft.EntityFrameworkCore.Tools --version 6.0.36
dotnet add package Pomelo.EntityFrameworkCore.MySql --version 6.0.3

操作数据迁移时需要执行EFCore的命令行工具,这需要用到dotnet ef命令,如果我们尚未安装该命令行工具,可以执行以下命令安装。

dotnet tool install --global dotnet-ef --version 6.0.36

创建数据模型和DbContext

下面代码我们创建了DbContext和自定义的数据模型Student

using Microsoft.EntityFrameworkCore;

namespace DemoWebAPI.Model;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    public DbSet<Student> Students { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 从程序集中引入所有数据表映射配置类
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
        base.OnModelCreating(modelBuilder);
    }
}
namespace DemoWebAPI.Model;

public class Student
{
    public long Id { get; set; }
    public string Name { get; set; }
    public int? Age { get; set; }
    public DateTime CreateTime { get; set; }
}

对于数据模型,我们采用FluentAPI进行配置。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace DemoWebAPI.Model;

public class StudentEntityConfig : IEntityTypeConfiguration<Student>
{
    public void Configure(EntityTypeBuilder<Student> builder)
    {
        builder.ToTable("t_student");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id).ValueGeneratedOnAdd().HasColumnName("id");
        builder.Property(x => x.Name).IsRequired().HasMaxLength(50).HasColumnName("name");
        builder.Property(x => x.Age).HasColumnName("age");
        builder.Property(x => x.CreateTime).IsRequired().HasColumnName("create_time");
    }
}

注册DbContext

Program.cs中,我们需要调用builder.Services.AddDbContext()方法注册EFCore需要使用的DbContext

// 集成EFCore相关服务
builder.Services.AddDbContext<AppDbContext>(options =>
{
    const string connStr = "Server=localhost;Port=3306;Database=netstore;User=root;Password=root;";
    options.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
});

代码中,我们指定了MySQL数据库服务器的连接串,出于节省篇幅考虑这里我们是将连接串直接写在代码里的,实际开发中建议配置在appsettings.json内并通过配置框架读取。

创建和执行数据迁移

如上配置好数据模型、DbContext并注册到ASP.NET Core后,我们就可以创建并执行数据迁移了,我们可以执行以下命令进行操作。

dotnet ef migrations add InitialCreate
dotnet ef database update

数据迁移执行后,我们可以查看数据库中的表结构,如果一切正常,我们可以看到自动创建的__EFMigrationsHistory和我们数据模型对应的t_student

操作数据记录

下面我们写一个简单的查询接口来演示EFCore在ASP.NET Core中的使用,我们这里将程序划分为控制器层和业务逻辑层两个部分,StudentController调用StudentService,具体操作数据实体类的代码在业务逻辑层执行。

StudentController.cs

using DemoWebAPI.Model;
using DemoWebAPI.Services;
using Microsoft.AspNetCore.Mvc;

namespace DemoWebAPI.Controllers;

[ApiController]
[Route("api/[controller]")]
public class StudentController : ControllerBase
{
    private readonly StudentService _studentService;

    public StudentController(StudentService studentService)
    {
        _studentService = studentService;
    }

    [HttpGet("[action]")]
    public ActionResult<ApiResult> GetStudentById([FromQuery] long id)
    {
        Student? student = _studentService.QueryStudentById(id);
        return ApiResult.Success(student);
    }
}

Program.cs

// 注册StudentService服务
builder.Services.AddScoped<StudentService>();

StudentService.cs

using DemoWebAPI.Model;

namespace DemoWebAPI.Services;

public class StudentService
{
    private AppDbContext _dbContext;

    public StudentService(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public Student? QueryStudentById(long id)
    {
        return _dbContext.Students.SingleOrDefault(s => s.Id == id);
    }
}

代码中,我们将AppDbContext通过依赖注入的方式注入到了StudentService中,QueryStudentById()方法接收一个GET参数作为查询条件,最终我们调用EFCore筛选指定主键的值。

使用事务

在ASP.NET Core中,我们可以使用TransactionScope来管理事务,确保数据库操作要么全部成功要么全部失败,这保证了数据的一致性和完整性,常用于涉及多个数据库操作并需要保证原子性的场景。

using DemoWebAPI.Model;
using System.Transactions;

namespace DemoWebAPI.Services;

public class StudentService
{
    private AppDbContext _dbContext;

    public StudentService(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public void DemoTransaction()
    {
        using TransactionScope scope = new TransactionScope(TransactionScopeOption.Required);
        // 操作1
        Student s1 = new()
        {
            Name = "汤姆",
            Age = 18,
            CreateTime = DateTime.Now
        };
        _dbContext.Students.Add(s1);
        _dbContext.SaveChanges();
        // 操作2
        Student s2 = new()
        {
            Name = "杰瑞",
            Age = 20,
            CreateTime = DateTime.Now
        };
        _dbContext.Students.Add(s2);
        _dbContext.SaveChanges();
        scope.Complete();
    }
}

代码中,我们在DemoTransaction()方法中启用了TransactionScope,如果事务操作完成,我们需要手动调用scope.Complete()告知提交事务,如果中间的操作中有某一步出错了,没有手动调用scope.Complete()时它就会自动回滚。

TransactionScopeOption.Required指定了当发生嵌套事务时的行为:

  • Required:如果存在外部事务,则加入外部事务;否则,创建一个新事务。大多数情况下使用这个选项。
  • RequiresNew:总是创建一个新事务,即使存在外部事务。
  • Suppress:不参与任何事务,当前操作会在没有事务的情况下执行。
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap