乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 数据持久化设计,基于Entity Framework Core和其广泛的数据库提供程序
前言
Entity Framework(EF)Core是轻量化、可扩展、开源和跨平台版的常用Entity Framework数据访问技术。
EF Core可用作对象关系映射程序(O/RM),这可以实现以下两点:
- 使.NET开发人员能够使用.NET对象处理数据库。
- 无需再像通常那样编写大部分数据访问代码。
作为《乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 适用于Entity Framework Core的命令行(CLI)工具集(Dotnet-EF)》以及《乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 浅析ASP.NET Core领域驱动设计,通过MediatR中介者模式实现CQRS和领域事件》的姊妹篇,这里将梳理在Entity Framework Core的广泛数据库提供程序支持下,如何实现数据库的Docker创建和简单对接。
常见数据库提供程序
Entity Framework Core可通过名为数据库提供程序的插件库访问许多不同的数据库。
数据库系统 | 配置示例 | NuGet 程序包 |
---|---|---|
SQL Server 或 Azure SQL | .UseSqlServer(connectionString) |
Microsoft.EntityFrameworkCore.SqlServer |
Azure Cosmos DB | .UseCosmos(connectionString, databaseName) |
Microsoft.EntityFrameworkCore.Cosmos |
SQLite | .UseSqlite(connectionString) |
Microsoft.EntityFrameworkCore.Sqlite |
EF Core 内存中数据库 | .UseInMemoryDatabase(databaseName) |
Microsoft.EntityFrameworkCore.InMemory |
PostgreSQL* | .UseNpgsql(connectionString) |
Npgsql.EntityFrameworkCore.PostgreSQL |
MySQL/MariaDB* | .UseMySql(connectionString) |
Pomelo.EntityFrameworkCore.MySql |
Oracle* | .UseOracle(connectionString) |
Oracle.EntityFrameworkCore |
MySQL | .UseMySQL(connectionString) |
MySql.EntityFrameworkCore |
Docker创建数据库实例
通过Docker准备PostgreSQL实例
PostgreSQL,通常简称为"Postgres",是一个对象关系型数据库管理系统(ORDBMS),强调可扩展性和标准符合性。作为一个数据库服务器,它的主要功能是安全地存储数据,并支持最佳实践,随后根据其他软件应用程序的要求进行检索,无论是同一台计算机上的软件还是在网络上的另一台计算机上运行的软件(包括互联网)。它可以处理从小型单机应用到有许多并发用户的大型面向互联网的应用的工作负荷。最近的版本还提供数据库本身的复制,以保证安全和可扩展性。
PostgreSQL实现了SQL:2011标准的大部分内容,符合ACID标准和事务性(包括大多数DDL语句),使用多版本并发控制(MVCC)避免了锁定问题,提供了对脏读和完全序列化的免疫力;使用许多其他数据库所没有的索引方法处理复杂的SQL查询;具有可更新视图和物化视图、触发器、外键;支持函数和存储过程以及其他可扩展性,并有大量由第三方编写的扩展。除了可以与主要的专有和开源数据库一起工作外,PostgreSQL还通过其广泛的标准SQL支持和可用的迁移工具,支持从这些数据库迁移。如果使用了专有的扩展,通过它的可扩展性,可以通过一些内置的和第三方的开放源码的兼容性扩展来模拟许多扩展,例如对Oracle的扩展。
准备一个PostgreSQL的Docker实例
docker run -d --name postgres --restart unless-stopped -p 5432:5432 -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=xxxxxxxxxxxxxx" postgres:14.5
默认的用户名是postgres
,默认端口是5432
docker exec -it postgres /bin/bash
通过Docker准备MSSQL实例
准备一个MSSQL(Microsoft SQL Server)的Docker实例
docker run -d --name mssql --restart unless-stopped -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=xxxxxxxxxxxxxxxx" mcr.microsoft.com/mssql/server:2022-latest
如果想要运行的是SQL Express版本,还可以追加参数MSSQL_PID
-e "MSSQL_PID=Express"
MSSQL_PID
其实也是用来控制安装版本的,也和授权有关系
Developer
(默认值)Express
Standard
Enterprise
EnterpriseCore
连接的账号名是SA
,密码是自己设置这个,端口是1433
如果忘记密码,进入容器实例之后,可以查看
docker exec -it mssql /bin/bash
ps -eax
通过Docker准备MYSQL实例
准备一个MYSQL的Docker实例
docker run -d --name mysql --restart unless-stopped -p 3306:3306 -e MYSQL_ROOT_PASSWORD=xxxxxxxxxxxxxxxx mysql:5.7.40
默认用户是root
,默认端口是3306
通过Docker准备Oracle实例
准备一个Oracle 11g的Docker实例
docker run -d --name oracle --restart unless-stopped -p 1521:1521 registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
默认用户是root
,默认密码是helowin
,默认端口1521
对接示例
建立示例领域和上下文
领域模型
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public List<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
上下文
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
建立示例项目(SQLite)
依赖包
https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
如果是
Net Core 3.1
项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.17
。
建立示例DbContext
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public string DbPath { get; }
public BloggingContext()
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, "blogging.db");
}
// The following configures EF to create a Sqlite database file in the
// special "local" folder for your platform.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
使用它
using (var db = new BloggingContext())
{
Console.WriteLine("Ensure Database Created");
db.Database.EnsureCreated();
Console.WriteLine($"DbPath:{db.DbPath}");
Console.WriteLine("Inserting a new blog");
var blog = new Blog
{
BlogId = 16839191,
Url = "https://www.cnblogs.com/taylorshi/p/16839191.html"
};
db.Add(blog);
db.SaveChanges();
}
Console.WriteLine("Hello World!");
前面使用的是在DbContext
内部去定义位置和连接字符串,实际上,可以从外面传进去。
public class PosttingContext : DbContext
{
public PosttingContext(DbContextOptions<PosttingContext> options)
: base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
这里需要构建一个公开的构造函数,通过这个入口就能将上下文配置从外部传进来。
我们试着修改下使用方式
static void Main(string[] args)
{
var folder = Environment.SpecialFolder.MyDocuments;
var path = Environment.GetFolderPath(folder);
var DbPath = System.IO.Path.Join(path, "EFSqliteConsole.db");
var services = new ServiceCollection();
services.AddDbContext<BloggingContext>(opt => opt.UseSqlite($"Data Source={DbPath}"));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<BloggingContext>();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var blog = new Blog
{
BlogId = new Random(16839191).Next(),
Url = "https://www.cnblogs.com/taylorshi/p/16843914.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
建立示例项目(SQLServer)
依赖包
https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
如果是
Net Core 3.1
项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.17
。
static void Main(string[] args)
{
var connectionString = "Server=tcp:localhost,1433;Database=TeslaOrder.EFSqlServerConsole;User Id=sa;Password=beE#Yahlj!Sdgj6x;";
var services = new ServiceCollection();
services.AddDbContext<BloggingContext>(opt => opt.UseSqlServer(connectionString));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<BloggingContext>();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var blog = new Blog
{
Url = "https://www.cnblogs.com/taylorshi/p/16843914.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
建立示例项目(InMemory)
依赖包
https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.EntityFrameworkCore.InMemory
如果是
Net Core 3.1
项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.17
。
static void Main(string[] args)
{
var databaseName = "EFInMeoryConsole";
var services = new ServiceCollection();
services.AddDbContext<BloggingContext>(opt => opt.UseInMemoryDatabase(databaseName));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<BloggingContext>();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var blog = new Blog
{
Url = "https://www.cnblogs.com/taylorshi/p/16843914.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
建立示例项目(Azure Cosmos DB)
依赖包
https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Cosmos
dotnet add package Microsoft.EntityFrameworkCore.Cosmos
如果是
Net Core 3.1
项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.17
。
static void Main(string[] args)
{
var connectionString = "AccountEndpoint=https://xxxxxxx.documents.azure.com:443/;AccountKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxlUFYU9lbHgWw3FTLNzQB1IDm1DZ5VGHZQwACDbS4IgGA==;";
var databaseName = "EFCosmosConsole";
var services = new ServiceCollection();
services.AddDbContext<BloggingContext>(opt => opt.UseCosmos(connectionString, databaseName));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<BloggingContext>();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var blog = new Blog
{
BlogId = new Random(99999).Next(),
Url = "https://www.cnblogs.com/taylorshi/p/16843914.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
建立示例项目(PostgreSQL)
依赖包
https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
如果是
Net Core 3.1
项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.10
。
static void Main(string[] args)
{
var connectionString = "Host=localhost;Port=5432;Database=EFPostgreSQLConsole;Username=postgres;Password=xxxxxxxxxxxxxxxxxxxxx;Pooling=true;";
var services = new ServiceCollection();
services.AddDbContext<BloggingContext>(opt => opt.UseNpgsql(connectionString));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<BloggingContext>();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var blog = new Blog
{
Url = "https://www.cnblogs.com/taylorshi/p/16843914.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
建立示例项目(MySQL)
依赖包
https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql
dotnet add package Pomelo.EntityFrameworkCore.MySql
如果是
Net Core 3.1
项目,最新的版本无法兼容,可以追加版本号参数--version 5.0.4
。
查看MYSQL版本,构建MySqlServerVersion
对象,如果是
select @@version as version;
static void Main(string[] args)
{
var connectionString = "server=localhost;port=16000;user=root;password=xxxxxxxxxxxxxx;database=EFMySQLPomeloConsole;charset=utf8mb4;ConnectionReset=false;Min Pool Size=10;Max Pool Size=200;";
var serverVersion = new MySqlServerVersion(new Version(5, 7, 40));
var services = new ServiceCollection();
services.AddDbContext<BloggingContext>(opt => opt.UseMySql(connectionString, serverVersion));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<BloggingContext>();
//context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var blog = new Blog
{
Url = "https://www.cnblogs.com/taylorshi/p/16843914.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
这里还可以配置重试机制
services.AddDbContext<BloggingContext>(opt =>
{
opt.UseMySql
(
connectionString,
serverVersion,
options => options.EnableRetryOnFailure
(
maxRetryCount: 3,
maxRetryDelay: System.TimeSpan.FromSeconds(10),
errorNumbersToAdd: new List<int> { 0 }
)
);
});
https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html
EnableRetryOnFailure
方法中errorNumbersToAdd
参数是用来设置错误代码的,只有设置了错误代码的错误,才会触发重试。获取错误代码的方法有很多种,通过异常信息进行获取,比如,使用MySql数据时,触发的异常类型是MySqlException
,此类的Number属性的值EnableRetryOnFailure
方法所需要的Number
简单数据库日志记录
默认情况下Entity Framework Core都可以和Microsoft.Extensions.Logging很好的配合,只需要在平时我们配置数据库的后面追加相关策略即可。
根据日志级别输出到控制台
services.AddDbContext<BloggingContext>(opt =>
opt.UseMySql(connectionString, serverVersion)
// 日志输出到控制台
.LogTo(Console.WriteLine, LogLevel.Information)
);
根据日志类别来输出到控制台
services.AddDbContext<BloggingContext>(opt =>
opt.UseMySql(connectionString, serverVersion)
// 日志输出到控制台
.LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Name })
);
系统会将每条日志消息分配到一个已命名的分层记录器类别,这些类别包括
类别 | 消息 |
---|---|
Microsoft.EntityFrameworkCore | 所有 EF Core 消息 |
Microsoft.EntityFrameworkCore.Database | 所有数据库交互 |
Microsoft.EntityFrameworkCore.Database.Connection | 使用数据库连接 |
Microsoft.EntityFrameworkCore.Database.Command | 使用数据库命令 |
Microsoft.EntityFrameworkCore.Database.Transaction | 使用数据库事务 |
Microsoft.EntityFrameworkCore.Update | 正在保存实体,不包括数据库交互 |
Microsoft.EntityFrameworkCore.Model | 所有模型和元数据交互 |
Microsoft.EntityFrameworkCore.Model.Validation | 模型验证 |
Microsoft.EntityFrameworkCore.Query | 查询,不包括数据库交互 |
Microsoft.EntityFrameworkCore.Infrastructure | 常规事件,例如上下文创建 |
Microsoft.EntityFrameworkCore.Scaffolding | 数据库反向工程 |
Microsoft.EntityFrameworkCore.Migrations | 迁移 |
Microsoft.EntityFrameworkCore.ChangeTracking | 更改跟踪交互 |
日志输出记录敏感数据
services.AddDbContext<BloggingContext>(opt =>
opt.UseMySql(connectionString, serverVersion)
// 日志输出记录敏感数据
.EnableSensitiveDataLogging()
);
日志输出记录详细异常
services.AddDbContext<BloggingContext>(opt =>
opt.UseMySql(connectionString, serverVersion)
// 日志输出记录详细异常
.EnableDetailedErrors()
);
启用上下文池提高吞吐
DbContext对用于和数据库打交道的上下文,创建和释放它在高性能场景下仍然存在可优化空间,可通过AddDbContextPool
来替代AddDbContext
以便启用上下文池。
services.AddDbContextPool<BloggingContext>(opt => opt.UseMySql(connectionString, serverVersion));
需要注意的是,AddDbContextPool
的最大保留实例数(>= EFCore 6.0 默认值为1024
,< EFCore 6.0 默认值为128
),一旦超过这个保留数,将不再缓存新的上下文实例,会恢复到非池模式进行创建。
但是如果上下文池的连接数超过了数据库连接池的连接数时,就可能引发数据库连接池连接数超限的问题,为了避免这个问题,要么我们将上下文池的默认值改小,要么在数据库连接字符串那里把数据库连接池的值改大。
对MYSQL和MSServer而言,数据库连接池最小值默认是0,最大值默认是100
var connectionString = "........;Min Pool Size=10;Max Pool Size=200;";
查看MYSQL的查询连接数
show processlist
使用加密Sqlite+EfCore
Sqlite的免费版默认是不支持加密的.
已知的Sqlite加密工具有
SQLCipher是一个开源的,基于免费版SQLite的加密数据库。它采用256-bit AES进行加密,主要的接口和SQLite相同,另外增加了一些加解密相关的接口。
依赖包
https://www.nuget.org/packages/SQLitePCLRaw.bundle_e_sqlcipher
dotnet add package Microsoft.Data.Sqlite.Core --version 5.0.17
dotnet add package SQLitePCLRaw.bundle_e_sqlcipher --version 2.1.2
使用SqliteConnectionStringBuilder
来创建带密码的SQLite数据库
internal class Program
{
static void Main(string[] args)
{
var folder = Environment.SpecialFolder.MyDocuments;
var path = Environment.GetFolderPath(folder);
var dbPath = System.IO.Path.Join(path, "postting.db");
var baseConnectionString = $"Data Source={dbPath}";
var oldPassword = "xxxxxxxxxxxxxxxx";
var connectionString = new SqliteConnectionStringBuilder(baseConnectionString)
{
Mode = SqliteOpenMode.ReadWriteCreate,
Password = oldPassword
}.ToString();
// 设置密码
using (SqliteConnection connection = new SqliteConnection(connectionString))
{
connection.Open();
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = @"CREATE TABLE Users (
ID INTEGER PRIMARY KEY AUTOINCREMENT
);";
cmd.ExecuteNonQuery();
}
}
Console.ReadKey();
}
}
这里使用SqliteConnectionStringBuilder
来构建一个带有密码的Sqlite连接字符串对象,然后使用Microsoft.Data.Sqlite
名下的SqliteConnection
来创建连接,特别注意的是,创建完之后,插入一张空表,不然可能会还是未加密的。
Sqlite可以通过PRAGMA
命令来进一步修改密码
internal class Program
{
static void Main(string[] args)
{
var folder = Environment.SpecialFolder.MyDocuments;
var path = Environment.GetFolderPath(folder);
var dbPath = System.IO.Path.Join(path, "postting.db");
var baseConnectionString = $"Data Source={dbPath}";
var oldPassword = "BkBqwG3ps25qQExj";
var connectionString = new SqliteConnectionStringBuilder(baseConnectionString)
{
Mode = SqliteOpenMode.ReadWriteCreate,
Password = oldPassword
}.ToString();
// 修改密码
var newPassword = "BkBqwG3ps25qQEx";
using (SqliteConnection connection = new SqliteConnection(connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT quote($newPassword);";
command.Parameters.AddWithValue("$newPassword", newPassword);
var quotedNewPassword = command.ExecuteScalar() as string;
command.CommandText = "PRAGMA rekey = " + quotedNewPassword;
command.Parameters.Clear();
command.ExecuteNonQuery();
}
}
Console.ReadKey();
}
}
基于EFCore来使用带有密码(SQLCipher加密机制)的Sqlite
依赖包
https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite
https://www.nuget.org/packages/SQLitePCLRaw.bundle_e_sqlcipher
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 5.0.17
dotnet add package SQLitePCLRaw.bundle_e_sqlcipher --version 2.1.2
internal class Program
{
static void Main(string[] args)
{
var folder = Environment.SpecialFolder.MyDocuments;
var path = Environment.GetFolderPath(folder);
var dbPath = System.IO.Path.Join(path, "postting.db");
var dbPassword = "BkBqwG3ps25qQExj";
var connectionString = $"Data Source={dbPath};Password={dbPassword};";
var services = new ServiceCollection();
services.AddDbContext<PosttingContext>(opt => opt.UseSqlite(connectionString));
using (var scope = services.BuildServiceProvider().CreateScope())
{
var context = scope.ServiceProvider.GetService<PosttingContext>();
context.Database.EnsureCreated();
var blog = new Blog
{
BlogId = new Random(16839191).Next(),
Url = "https://www.cnblogs.com/taylorshi/p/16839191.html"
};
context.Add(blog);
context.SaveChanges();
var blogs = context.Blogs.ToList();
if (blogs.Any())
{
}
}
Console.ReadKey();
}
}
如何在已激活后的Navicat 16中打开它呢?
先运行或者编译程序,前往bin\Debug\netcoreapp3.1\runtimes\win-x64\native
目录
将e_sqlcipher.dll
改名成sqlite3.dll
,然后将改名后的sqlite3.dll
复制替换C:\Program Files\PremiumSoft\Navicat Premium 16
目录下的sqlite3.dll
即可
接下来新建SQLite 3的连接
并在高级中填写密码
就可以打开了。
参考
- Entity Framework Core
- EF Core 异步编程注意要点
- netcore3.1连接MySQL时报错: Method ‘Create‘ in type ... does not have an implementation的解决办法
- .net core 注册多个dbcontext注意事项
- 好大一个坑: EF Core 异步读取大字符串字段比同步慢100多倍
- Entity Framework Core的贴心:优雅处理带默认值的数据库字段
- Entity Framework Core的坑:Skip/Take放在Select之前造成Include的实体全表查询
- 被Entity Framework Core的细节改进震撼了一下
- 对Entity Framework Core的一次误会:实体状态不跟踪
- 使用Entity Framework Core需要注意的一个全表查询问题
- EF(Entity Framework)多对多关系下用LINQ实现"NOT IN"查询
- Why do we need to set Min pool size in ConnectionString
- EFCore 使用DbContextPool提高EfCore查询性能
- EF Core 小坑:DbContextPool 会引起数据库连接池连接耗尽
- TargetParameterCountException when enumerating through properties of string
- efcore技巧贴-也许有你不知道的使用技巧
- SqlConnectionStringBuilder类
- DbContext生存期、配置和初始化
- 加密
- .NET+Sqlite如何支持加密
- Microsoft.Data.Sqlite连接字符串
- FreeSql.Provider.SqliteCore如何加密
- 关于SQLite加密,看这篇就够了
- 再见收费的Navicat!操作所有数据库就靠它了!
- Navicat Premium 16 永久破解激活
- https://dbeaver.io
- Navicat.zip
- EFCore 使用Sqlite(带加密)
- Password protected SQLite with Entity Framework Core
- The DbContext of type cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions
- EF Core Azure Cosmos DB Provider
- 数据库提供程序
- Npgsql Entity Framework Core Provider
- EntityFramework Core使用PostgreSQL
- MySQL .NET Connection String Options
- Pomelo.EntityFrameworkCore.MySql-Configuration Options
- Entity Framework Core-简单的日志记录
- .Net EntityFramework连接Mysql连接池配置
- Chapter 2 Server Error Message Reference
- 使用 EF Core 的 EnableRetryOnFailure 解决短暂的数据库连接失败问题
- Consider enabling transient error resiliency by adding 'EnableRetryOnFailure()' to the 'UseMySql' call
- https://developer.aliyun.com/mirror/
- Docker安装Oracle数据库