前言
Entity Framework Core可通过名为数据库提供程序的插件库访问许多不同的数据库。
作为《乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 适用于Entity Framework Core的命令行(CLI)工具集(Dotnet-EF)》以及《乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 浅析ASP.NET Core领域驱动设计,通过MediatR中介者模式实现CQRS和领域事件》的姊妹篇,这里将梳理在Entity Framework Core的广泛数据库提供程序支持下,如何实现数据库的Docker创建和简单对接。
常见数据库提供程序
数据库系统 | 配置示例 | 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 |
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
对接示例
建立示例领域和上下文
领域模型
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();
}
使用加密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的连接
并在高级中填写密码
就可以打开了。
参考
- 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