乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 适用于Entity Framework Core的命令行(CLI)工具集(Dotnet-EF)

什么是EFCore CLI

适用于Entity Framework Core的命令行接口(CLI)工具可执行设计时开发任务。例如,可以创建迁移、应用迁移,并为基于现有数据库的模型生成代码。

image

获取EFCore CLI

https://github.com/TaylorShi/HelloEfCoreCli

安装EFCore CLI

全局安装

dotnet tool install --global dotnet-ef

image

更新EFCore CLI

dotnet tool update --global dotnet-ef

image

卸载EFCore CLI

dotnet tool uninstall --global dotnet-ef

image

验证EFCore CLI

dotnet ef

image

示范项目

详细见:乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 数据持久化设计,基于Entity Framework Core和其广泛的数据库提供程序

管理迁移

前置条件

通常使用EFCore CLI之前,我们需要确保项目已经安装了Microsoft.EntityFrameworkCore.Design,否则将会提示你

Your startup project 'xxxxxxxxxxx.EFSqliteConsole' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is correct, install the package, and try again.

终端切换到项目根目录,使用命令行安装它

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design

dotnet add package Microsoft.EntityFrameworkCore.Design

如果是Net Core 3.1项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.17 或者降级为--version 3.1.0

添加初始化迁移

dotnet ef migrations add $name

例如:

dotnet ef migrations add InitCreate

image

这个动作会创建一些迁移用的.cs文件,默认存放在项目根目录的Migrations文件夹中。

image

可选项

  • 如果要修改这个目录路径,可以使用参数:--output-dir|-o指定。
  • 如果要指定生成类的命名空间,可以使用参数:--namespace|-n指定。

如果你有多个DbContext则需要指定下

dotnet ef migrations add InitCreate --context $TargetContext

image

如果遇到将DbContext采用独立类库项目存放的情况,但是其它连接信息都在启动项目,可能会遇到一个报错

Your target project 'Tesla.Gooding.Interface' doesn't match your migrations assembly 'Tesla.Gooding.Infrastructure'. Either change your target project or change your migrations assembly.
Change your migrations assembly by using DbContextOptionsBuilder. E.g. options.UseSqlServer(connection, b => b.MigrationsAssembly("Tesla.Gooding.Interface")). By default, the migrations assembly is the assembly containing the DbContext.
Change your target project to the migrations project by using the Package Manager Console's Default project drop-down list, or by executing "dotnet ef" from the directory containing the migrations project.

这时候不要慌,可以在注册DbContext的时候,告诉它,我要在哪个程序集来执行操作。

// 添加主上下文
services.AddDbContext<GoodingMasterContext>(optionsAction =>
{
    optionsAction.UseMySql(masterConnectionString, serverVersion, options =>
    {
        options.EnableRetryOnFailure
        (
            maxRetryCount: 3,
            maxRetryDelay: TimeSpan.FromSeconds(10),
            errorNumbersToAdd: new List<int> { 0 }
        );
        options.MigrationsAssembly("Tesla.Gooding.Interface");
    })
    // 根据日志级别输出到控制台
    .LogTo(Console.WriteLine, LogLevel.Information)
    // 日志输出记录敏感数据
    //.EnableSensitiveDataLogging()
    // 日志输出记录详细异常
    .EnableDetailedErrors();
});

这里options.MigrationsAssembly("Tesla.Gooding.Interface")便是告诉它,我们要从Tesla.Gooding.Interface项目来执行迁移管理。

列出可用的迁移

dotnet ef migrations list

image

可选项

  • 如果要指定用于连接到数据库的连接字符串,可以使用参数:--connection指定,默认为AddDbContextOnConfiguring中指定的值。
  • 如果要指定不要连接到数据库,可以使用参数:--no-connect指定。

回退上次的迁移

删除上一次迁移,回退为上一次迁移所做的代码更改。

dotnet ef migrations remove

image

可选项

  • 如果要如果连接到数据库时出错,则继续仅回退到代码更改,可以使用参数:--force|-f指定。

如果你有多个DbContext则需要指定下

dotnet ef migrations remove --context $TargetContext

有时候回退其实可能会失败

The migration '20221107171311_AddBrandTable' has already been applied to the database. Revert it and try again. If the migration has been applied to other databases, consider reverting its changes using a new migration instead.

这时候如果多次重试都不行,我们可以在DbContext把新增或者修改的几个部分先注释掉

public DbSet<Brand> Brands { get; set; }

//public DbSet<BrandImage> BrandImages { get; set; }

//public DbSet<BrandUrl> BrandUrls { get; set; }

然后创建一个撤回的迁移RevertBrandTable

dotnet ef migrations add RevertBrandTable --context GoodingMasterContext

image

它会生成Drop表的操作,接下来我们调用更新即可

dotnet ef database update 20221107171836_RevertBrandTable --context GoodingMasterContext

image

这样其实也达到了回退的效果。

从迁移生成SQL脚本

我们可以基于迁移得到SQL脚本

dotnet ef migrations script $fromIndex $name

这里$fromIndex默认值就是0

可选项

  • 如果要指定生成脚本的文件路径,可以使用参数:--output|-o指定。
  • 如果要生成可在任何迁移时用于数据库的脚本,可以使用参数:--idempotent|-i指定。
  • 不要生成SQL事务语句,可以使用参数:--no-transactions指定。

例如从InitCreate的首次迁移前到它自身包括的SQL脚本

dotnet ef migrations script 0 InitCreate

image

这时候我们给Blog领域模型增加一个Title字段

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public string Title { get; set; }

    public List<Post> Posts { get; } = new List<Post>();
}

并且我们创建一个新的迁移UpdateBlogTitle

dotnet ef migrations add UpdateBlogTitle

image

这个时候,按道理就应该有两个迁移了,那么从InitCreate之后迁移的所有SQL脚本,就能够生成第二次UpdateBlogTitle的SQL脚本了

dotnet ef migrations script InitCreate

image

确实结果是这样的。

创建可执行文件更新数据库

dotnet ef migrations bundle

需要 >=Entity Framework Core 6.0.0

可选项

  • 如果要指定要创建的可执行文件的路径,可以使用参数:--output|-o指定。
  • 如果要覆盖现有文件,可以使用参数:--force|-f指定
  • 如果要同时绑定.NET 运行时,因此不需要在计算机上安装它,可以使用参数:--self-contained指定
  • 如果要要绑定的目标运行时,可以使用参数:--target-runtime指定

管理上下文

获取DbContext信息

dotnet ef dbcontext info

image

列出可用DbContext

dotnet ef dbcontext list

image

生成DbContext优化模型

dotnet ef dbcontext optimize

需要 >=Entity Framework Core 6.0.0

根据数据库生成代码

dotnet ef dbcontext scaffold $Connection $Provider

其中

  • $Connection,用于连接到数据库的连接字符串。
  • $Provider,要使用的提供程序,通常,这是NuGet包的名称,例如Microsoft.EntityFrameworkCore.SqlServer

可选项

  • 如果要使用属性配置模型,可以使用参数:--data-annotations|-d指定。
  • 如果要指定生成的DbContext类的名称,可以使用参数:--context指定。
  • 如果要指定放置DbContext类文件的目录,可以使用参数:--context-dir指定。
  • 如果要指定生成的DbContext类的命名空间,可以使用参数:--context-namespace指定。
  • 如果要覆盖现有文件,可以使用参数:--force|-f指定。
  • 如果要指定放置实体类文件的目录,可以使用参数:--output-dir|-o指定。
  • 如果要指定所有生成的类的命名空间,可以使用参数:--namespace|-n指定。
  • 如果要指定生成实体类型的表的架构,可以使用参数:--schema指定。
  • 如果要指定为其生成实体类型的表,可以使用参数:--table|-t指定。
  • 如果要使用与数据库中显示的名称完全相同的表和列名,可以使用参数:--use-database-names指定。
  • 如果要禁止在生成的DbContext类中生成OnConfiguring方法,可以使用参数:--no-onconfiguring指定。
  • 请勿使用复数化程序,可以使用参数:--no-pluralize指定。

例如

dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models
dotnet ef dbcontext scaffold "DataSource=C:\Users\TaylorShi\AppData\Local\blogging.db" Microsoft.EntityFrameworkCore.Sqlite -o Models

image

image

dotnet ef dbcontext scaffold "DataSource=C:\Users\TaylorShi\AppData\Local\blogging.db" Microsoft.EntityFrameworkCore.Sqlite -o Models -t Blogs -t Posts --context-dir Context -c BlogContext --context-namespace TeslaOrder.EFSqliteConsole

image

image

public partial class BlogContext : DbContext
{
    public BlogContext()
    {
    }

    public BlogContext(DbContextOptions<BlogContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Blog> Blogs { get; set; }
    public virtual DbSet<Post> Posts { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
            optionsBuilder.UseSqlite("DataSource=C:\\Users\\TaylorShi\\AppData\\Local\\blogging.db");
        }
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>(entity =>
        {
            entity.HasIndex(e => e.BlogId, "IX_Posts_BlogId");

            entity.HasOne(d => d.Blog)
                .WithMany(p => p.Posts)
                .HasForeignKey(d => d.BlogId);
        });

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

从DbContext生成SQL脚本

dotnet ef dbcontext script

可选项

  • 如果要指定生成的SQL脚本文件,可以使用参数:--output|-o指定。
  • 如果要指定DbContext,可以使用参数:--context|-c指定。

image

管理数据库

根据迁移更新数据库

dotnet ef database update $name

其中$name可以指定迁移的迁移名称。

可选项

  • 如果要指定用于连接到数据库的连接字符串,可以使用参数:--connection指定,默认为AddDbContextOnConfiguring中指定的值。

例如,不带参数代表开始首次迁移之前并会还原所有迁移

dotnet ef database update

image

image

dotnet ef database update 20221031043138_UpdateBlogName

image

另外>=Entity Framework Core 5.0.0还支持将其他参数传递到Program.CreateHostBuilder

dotnet ef database update -- --environment Production

其中--之后所有内容都会被视为参数,它将转发给应用去处理。

如果你有多个DbContext则需要指定下

dotnet ef database update --context $TargetContext

image

dotnet ef database update 20221107171311_AddBrandTable --context GoodingMasterContext

image

删除数据库

dotnet ef database drop

可选项

  • 如果不需要确认,直接删除,可以使用参数:--force|-f指定。
  • 如果显示要删除的数据库,但不删除它,可以使用参数:--dry-run指定。

image

识别和解析数据库连接字符串

识别SQlServer连接字符串

依赖包

https://www.nuget.org/packages/System.Data.SqlClient

dotnet add package System.Data.SqlClient

可以使用System.Data.SqlClient包下的SqlConnectionStringBuilder进行解析

internal class Program
{
    static void Main(string[] args)
    {
        var connectionString = "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;";
        SqlConnectionStringBuilder builder =  new SqlConnectionStringBuilder(connectionString);
        var properties = builder.GetType().GetProperties();
        foreach (var property in properties)
        {
            if (property.CanRead)
            {
                if (property.GetIndexParameters().Length == 0)
                {
                    Console.WriteLine($"{property.Name}:{property.GetValue(builder) ?? ""}");
                }
            }
        }
        Console.ReadKey();
    }
}

image

查看SqlConnectionStringBuilder定义,它还继承了DbConnectionStringBuilder

namespace System.Data.SqlClient
{
    [DefaultMember("Item")]
    public sealed class SqlConnectionStringBuilder : DbConnectionStringBuilder
    {

        public SqlConnectionStringBuilder();

        public SqlConnectionStringBuilder(string connectionString);

        public override object this[string keyword] { get; set; }

        // 连接到SQL Server可用性组中的数据库时声明应用程序工作负荷类型。
        public ApplicationIntent ApplicationIntent { get; set; }

        // 获取或设置与连接字符串关联的应用程序的名称
        public string ApplicationName { get; set; }

        // 在确定有一个空闲的连接失败之后尝试重新连接的次数。该值必须是介于0和255之间的整数。默认值为1。设置为0将禁止在空闲连接失败时重新连接。
        public int ConnectRetryCount { get; set; }

        // 标识空闲连接失败之后每次重新连接尝试之间的时间量(以秒为单位)。此值必须是介于1和60之间的整数。默认值为10秒。
        public int ConnectRetryInterval { get; set; }

        // 获取或设置在终止尝试并产生错误之前,等待与服务器连接的时间长度(以秒为单位)
        public int ConnectTimeout { get; set; }

        // 获取或设置包含主数据文件名称的字符串,可附加数据库的完整路径名。
        public string AttachDBFilename { get; set; }

        // 获取或设置SQL Server语言记录名称
        public string CurrentLanguage { get; set; }

        // 获取或设置要连接到的SQL Server实例的名称或网络地址
        public string DataSource { get; set; }

        // 获取或设置一个布尔值,该值指示在服务器安装了证书的情况下,SQL Server是否为客户端和服务器之间发送的所有数据使用SSL加密。
        public bool Encrypt { get; set; }

        // 获取或设置一个布尔值,该值指示SQL Server连接池程序是否在创建线程的当前事务上下文中自动登记连接。
        public bool Enlist { get; set; }

        // 获取或设置在主服务器停机时要连接到的伙伴服务器的名称或地址。
        public string FailoverPartner { get; set; }

        // 获取或设置与连接关联的数据库的名称。
        public string InitialCatalog { get; set; }

        // 获取或设置一个布尔值,该值指示是否在连接中指定用户ID和密码(值为false时),或者是否使用当前的Windows帐户凭据进行身份验证(值为true时)。
        public bool IntegratedSecurity { get; set; }

        // 获取包含ICollection中的键的SqlConnectionStringBuilder
        public override ICollection Keys { get; }

        // 获取或设置连接被销毁前在连接池中存活的最短时间(以秒为单位)
        public int LoadBalanceTimeout { get; set; }

        // 获取或设置针对此特定连接字符串连接池中所允许的最大连接数
        public int MaxPoolSize { get; set; }

        // 获取或设置针对此特定连接字符串连接池中所允许的最小连接数
        public int MinPoolSize { get; set; }

        // 如果为true,则应用程序可以维护多个活动结果集(MARS)。 如果为false,则应用程序必须先处理或取消某一批中的所有结果集,才可以在该连接上执行任何其他批次
        public bool MultipleActiveResultSets { get; set; }

        // 如果应用程序连接到不同子网上的Always On可用性组(AG)或Always On故障转移群集实例(FCI) ,则设置MultiSubnetFailover=true可更快地检测和连接到当前活动服务器的
        public bool MultiSubnetFailover { get; set; }

        // 获取或设置用来与SQL Server的实例通信的网络数据包的大小(以字节为单位)
        public int PacketSize { get; set; }

        // 获取或设置SQL Server帐户的密码
        public string Password { get; set; }

        // 获取或设置一个值,该值指示是否应返回安全敏感信息(如密码或访问令牌)作为在连接处于打开状态后创建的SqlConnectionStringBuilder连接的连接字符串的一部分
        public bool PersistSecurityInfo { get; set; }

        // 连接池的暂停时段行为
        public PoolBlockingPeriod PoolBlockingPeriod { get; set; }

        // 获取或设置一个布尔值,该值指示每次请求连接时该连接是汇入连接池还是显式打开
        public bool Pooling { get; set; }

        // 获取或设置一个布尔值,该值指示是否使用连接来支持复制
        public bool Replication { get; set; }

        // 获取或设置一个字符串值,该值表示连接如何保持与登记的System.Transactions事务的关联
        public string TransactionBinding { get; set; }

        // 获取或设置一个值,该值指示在跳过用于验证信任的证书链遍历时是否加密信道
        public bool TrustServerCertificate { get; set; }

        // 获取或设置一个字符串值,该值指示应用程序所需的类型系统
        public string TypeSystemVersion { get; set; }

        // 获取或设置连接到SQL Server时要使用的用户ID
        public string UserID { get; set; }

        // 获取或设置一个值,该值指示是否将连接从默认的SQL Server Express实例重定向到在调用方帐户之下运行并且在运行时启动的实例
        public bool UserInstance { get; set; }

        // 获取一个包含ICollection中的值的SqlConnectionStringBuilder
        public override ICollection Values { get; }

        // 获取或设置连接到 SQL Server 的工作站的名称
        public string WorkstationID { get; set; }

        public override void Clear();

        public override bool ContainsKey(string keyword);

        public override bool Remove(string keyword);

        public override bool ShouldSerialize(string keyword);

        public override bool TryGetValue(string keyword, out object value);
    }
}

参考

posted @ 2022-10-31 12:46  TaylorShi  阅读(374)  评论(0编辑  收藏  举报