乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 适用于Entity Framework Core的命令行(CLI)工具集(Dotnet-EF)
什么是EFCore CLI
适用于Entity Framework Core的命令行接口(CLI)工具可执行设计时开发任务。例如,可以创建迁移、应用迁移,并为基于现有数据库的模型生成代码。
获取EFCore CLI
安装EFCore CLI
全局安装
dotnet tool install --global dotnet-ef
更新EFCore CLI
dotnet tool update --global dotnet-ef
卸载EFCore CLI
dotnet tool uninstall --global dotnet-ef
验证EFCore CLI
dotnet ef
示范项目
详细见:乘风破浪,遇见最佳跨平台跨终端框架.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
这个动作会创建一些迁移用的.cs
文件,默认存放在项目根目录的Migrations
文件夹中。
可选项
- 如果要修改这个目录路径,可以使用参数:
--output-dir|-o
指定。 - 如果要指定生成类的命名空间,可以使用参数:
--namespace|-n
指定。
如果你有多个DbContext则需要指定下
dotnet ef migrations add InitCreate --context $TargetContext
如果遇到将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
可选项
- 如果要指定用于连接到数据库的连接字符串,可以使用参数:
--connection
指定,默认为AddDbContext
或OnConfiguring
中指定的值。 - 如果要指定不要连接到数据库,可以使用参数:
--no-connect
指定。
回退上次的迁移
删除上一次迁移,回退为上一次迁移所做的代码更改。
dotnet ef migrations remove
可选项
- 如果要如果连接到数据库时出错,则继续仅回退到代码更改,可以使用参数:
--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
它会生成Drop表的操作,接下来我们调用更新即可
dotnet ef database update 20221107171836_RevertBrandTable --context GoodingMasterContext
这样其实也达到了回退的效果。
从迁移生成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
这时候我们给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
这个时候,按道理就应该有两个迁移了,那么从InitCreate
之后迁移的所有SQL脚本,就能够生成第二次UpdateBlogTitle
的SQL脚本了
dotnet ef migrations script InitCreate
确实结果是这样的。
创建可执行文件更新数据库
dotnet ef migrations bundle
需要
>=Entity Framework Core 6.0.0
可选项
- 如果要指定要创建的可执行文件的路径,可以使用参数:
--output|-o
指定。 - 如果要覆盖现有文件,可以使用参数:
--force|-f
指定 - 如果要同时绑定.NET 运行时,因此不需要在计算机上安装它,可以使用参数:
--self-contained
指定 - 如果要要绑定的目标运行时,可以使用参数:
--target-runtime
指定
管理上下文
获取DbContext信息
dotnet ef dbcontext info
列出可用DbContext
dotnet ef dbcontext list
生成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
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
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
指定。
管理数据库
根据迁移更新数据库
dotnet ef database update $name
其中$name
可以指定迁移的迁移名称。
可选项
- 如果要指定用于连接到数据库的连接字符串,可以使用参数:
--connection
指定,默认为AddDbContext
或OnConfiguring
中指定的值。
例如,不带参数代表开始首次迁移之前并会还原所有迁移
dotnet ef database update
dotnet ef database update 20221031043138_UpdateBlogName
另外>=Entity Framework Core 5.0.0
还支持将其他参数传递到Program.CreateHostBuilder
中
dotnet ef database update -- --environment Production
其中--
之后所有内容都会被视为参数,它将转发给应用去处理。
如果你有多个DbContext则需要指定下
dotnet ef database update --context $TargetContext
dotnet ef database update 20221107171311_AddBrandTable --context GoodingMasterContext
删除数据库
dotnet ef database drop
可选项
- 如果不需要确认,直接删除,可以使用参数:
--force|-f
指定。 - 如果显示要删除的数据库,但不删除它,可以使用参数:
--dry-run
指定。
识别和解析数据库连接字符串
识别SQlServer连接字符串
依赖包
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();
}
}
查看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);
}
}