• Asp-Net-Core开发笔记:快速在已有项目中引入EFCore


    前言#

    很多项目一开始选型的时候没有选择EFCore,不过EFCore确实好用,也许由于种种原因后面还是需要用到,这时候引入EFCore也很方便。

    本文以 StarBlog 为例,StarBlog 目前使用的 ORM 是 FreeSQL ,引入 EFCore 对我来说最大的好处是支持多个数据库,如果是 FreeSQL 的话,服务注册的时候是单例模式,只能连接一个数据库,如果需要使用 FreeSQL 同时连接多个数据库,需要自行做一些额外的工作。

    要实现的效果是:把访问记录单独使用一个数据库来存储,并且使用 EFCore 管理。

    安装工具#

    首先安装 EFCore 的 cli 工具

    dotnet tool install --global dotnet-ef
    

    项目架构#

    先来回顾一下项目架构:基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目

    StarBlog
    ├── StarBlog.Contrib
    ├── StarBlog.Data
    ├── StarBlog.Migrate
    ├── StarBlog.Web
    └── StarBlog.sln
    

    为了解耦,和数据有关的代码在 StarBlog.Data 项目下,因此引入 EFCore 只需要在 StarBlog.Data 这个项目中添加依赖即可。

    添加依赖#

    StarBlog.Data 项目中添加以下三个依赖

    • Microsoft.EntityFrameworkCore
    • Microsoft.EntityFrameworkCore.Sqlite
    • Microsoft.EntityFrameworkCore.Tools

    EFCore 对 SQLite 的支持很弱(根本原因是微软提供的 SQLite 驱动功能太少),所以只适合在本地开发玩玩,实际部署还是得切换成 C/S 架构的数据库(PgSQL/MySQL/SQL Server)才行。

    添加后项目的 .csproj 文件新增的依赖类似这样

    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.18" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.18" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.18">
      <PrivateAssets>allPrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitiveIncludeAssets>
    PackageReference>
    

    目前 StarBlog 还在使用 .Net6 所以我添加的 EFCore 是 6.x 版本,等后续 .Net8 正式版发布之后,我会把这个项目升级到 .Net8

    创建 DbContext#

    DbContext 是 EFCore 与数据库交互的入口,一般一个数据库对应一个。

    现在来 StarBlog.Data 项目下创建一个。

    using Microsoft.EntityFrameworkCore;
    using StarBlog.Data.Models;
    
    namespace StarBlog.Data;
    
    public class AppDbContext : DbContext {
      public DbSet VisitRecords { get; set; }
      
      public AppDbContext(DbContextOptions options) : base(options) { }
    }
    

    因为只需要让 EFCore 管理访问记录,所以只需要一个 DbSet

    实体类配置#

    然后来创建个配置,虽然也可以用 Data Annotation 来配置,但 EFCore 推荐使用 Fluent Config 方式来配置数据表和字段。

    创建 StarBlog.Data/Config/VisitRecordConfig.cs 文件

    public class VisitRecordConfig : IEntityTypeConfiguration {
      public void Configure(EntityTypeBuilder builder) {
        builder.ToTable("visit_record");
        builder.HasKey(e => e.Id);
        builder.Property(e => e.Ip).HasMaxLength(64);
        builder.Property(e => e.RequestPath).HasMaxLength(2048);
        builder.Property(e => e.RequestQueryString).HasMaxLength(2048);
        builder.Property(e => e.RequestMethod).HasMaxLength(10);
        builder.Property(e => e.UserAgent).HasMaxLength(1024);
      }
    }
    

    主要是配置了主键和各个字段的长度。

    数据类型这块 EFCore 会自动映射,具体请参考官方文档。

    主键类型选择#

    这里插播一下题外话,关于主键类型应该如何选择。

    目前主要有几种方式:

    • 自增
    • GUID
    • 自增+GUID
    • Hi/Lo

    这几种方式各有优劣。

    • 自增的好处是简单,缺点是在数据库迁移或者分布式系统中容易出问题,而且高并发时插入性能较差。
    • GUID好处也是简单方便,而且也适用于分布式系统;MySQL的InnoDB引擎强制主键使用聚集索引,导致新插入的每条数据都要经历查找合适插入位置的过程,在数据量大的时候插入性能很差。
    • 自增+GUID是把自增字段作为物理主键,GUID作为逻辑主键,可以在一定程度上解决上述两种方式的问题。
    • Hi/Lo可以优化自增列的性能,但只有部分数据库支持,比如SQL Server,其他的数据库暂时还没研究。

    DesignTime 配置#

    因为我们的项目是把 AspNetCore 和数据分离的,所以需要一个 DesignTime 配置来让 EFCore 知道如何执行迁移。

    StarBlog.Data 中创建 AppDesignTimeDbContextFactory.cs 文件

    public class AppDesignTimeDbContextFactory : IDesignTimeDbContextFactory {
      public AppDbContext CreateDbContext(string[] args) {
        var builder = new DbContextOptionsBuilder();
    
        var connStr = Environment.GetEnvironmentVariable("CONNECTION_STRING");
        if (connStr == null) {
          var dbpath = Path.Combine(Environment.CurrentDirectory, "app.log.db");
          connStr = $"Data Source={dbpath};";
        }
    
        builder.UseSqlite(connStr);
        return new AppDbContext(builder.Options);
      }
    }
    

    这里从环境变量读取数据库连接字符串,如果读不到就使用默认的数据库文件。

    数据库迁移#

    这块主要是使用两组命令

    • migrations - 用于监控数据库的修改
    • database - 将修改同步到数据库里

    cd 到 StarBlog.Data 目录下,执行

    dotnet ef migrations add InitialCreate -o Migrations 
    

    之后可以看到 Migrations 目录下生成了迁移的代码

    如果需要指定数据库文件,可以设置环境变量。

    Windows的使用:

    set CONNECTION_STRING = "Data Source=app.db;"
    

    Linux的也差不多,把 set 换成 export

    export CONNECTION_STRING = "Data Source=app.db;"
    

    运行以下命令同步到数据库

    dotnet ef database update
    

    执行之后就会在 StarBlog.Data 下生成 SQLite 数据库文件。

    在AspNetCore项目里集成EFCore#

    先把数据库连接字符串写到配置文件 appsettings.json

    {
      "ConnectionStrings": {
        "SQLite": "Data Source=app.db;Synchronous=Off;Cache Size=5000;",
        "SQLite-Log": "Data Source=app.log.db;"
      }
    }
    

    Program.cs 里注册服务

    builder.Services.AddDbContext(options => {
      options.UseSqlite(builder.Configuration.GetConnectionString("SQLite-Log"));
    });
    

    搞定~

    db-first#

    从已有数据库生成实体类,一般新项目不推荐这种开发方式,不过在旧项目上使用还是比较方便的,EFCore 的 cli tool 也提供很丰富的代码生成功能。

    这里提供一下例子:

    • 使用 PostgreSql 数据库,要把其中 pe_shit_data 库的所有表生成实体类
    • 生成的 DbContext 类名为 ShitDbContext
    • DbContext 类的命名空间为 PE.Data
    • 实体类放在 ShitModels 目录下,命名空间为 PE.Data.ShitModels

    命令如下

    dotnet ef dbcontext scaffold `
        "Host=cuc.dou3.net;Database=pe_shit_data;Username=postgres;Password=passw0rd" `
        Npgsql.EntityFrameworkCore.PostgreSQL `
        -f `
        -c ShitDbContext `
        --context-dir . `
        --context-namespace PE.Data `
        -o ShitModels `
        --namespace PE.Data.ShitModels `
    

    这个是 powershell 的命令,如果是 Linux 环境,把每一行命令末尾的反引号换成 \ 即可。

    参考资料#

  • 相关阅读:
    大数据扫黄,是真的吗?
    「Windows Bug」Window 系统下双网卡内外网冲突
    [Leetcode]13. 罗马数字转整数
    JAVA基础算法(11)----- 最大二叉树
    vue: 使用下拉树组件@riophae/vue-treeselect
    【Jenkins高级操作,欢迎阅读】
    C++数据结构AVL树
    云扩RPA研习社 | 浅析自动化原理(上)
    C# SortedList类:有序列表
    硬盘驱动器(HDD)伺服系统鲁棒控制
  • 原文地址:https://www.cnblogs.com/deali/p/17749676.html