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

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

时间:2022-11-05 23:12:58浏览次数:75  
标签:Core https 跨平台 context var new Net public

前言

Entity Framework Core可通过名为数据库提供程序的插件库访问许多不同的数据库。

image

作为《乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 适用于Entity Framework Core的命令行(CLI)工具集(Dotnet-EF)》以及《乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 浅析ASP.NET Core领域驱动设计,通过MediatR中介者模式实现CQRS和领域事件》的姊妹篇,这里将梳理在Entity Framework Core的广泛数据库提供程序支持下,如何实现数据库的Docker创建和简单对接。

https://github.com/TaylorShi/HelloEfCoreProvider

常见数据库提供程序

数据库系统 配置示例 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的扩展。

image

准备一个PostgreSQL的Docker实例

https://hub.docker.com/_/postgres

docker run -d --name postgres --restart unless-stopped -p 5432:5432 -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=xxxxxxxxxxxxxx" postgres:14.5

image

image

image

默认的用户名是postgres,默认端口是5432

docker exec -it postgres /bin/bash

image

通过Docker准备MSSQL实例

准备一个MSSQL(Microsoft SQL Server)的Docker实例

https://hub.docker.com/_/microsoft-mssql-server

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

image

如果想要运行的是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

image

通过Docker准备MYSQL实例

准备一个MYSQL的Docker实例

https://hub.docker.com/_/mysql

docker run -d --name mysql --restart unless-stopped -p 3306:3306 -e MYSQL_ROOT_PASSWORD=xxxxxxxxxxxxxxxx mysql:5.7.40

image

对接示例

建立示例领域和上下文

领域模型

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();
}

image

建立示例项目(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();
}

image

建立示例项目(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();
}

image

建立示例项目(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();
}

image

使用加密Sqlite+EfCore

Sqlite的免费版默认是不支持加密的.

已知的Sqlite加密工具有

SQLCipher是一个开源的,基于免费版SQLite的加密数据库。它采用256-bit AES进行加密,主要的接口和SQLite相同,另外增加了一些加解密相关的接口。

依赖包

https://www.nuget.org/packages/Microsoft.Data.Sqlite.Core

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目录

image

e_sqlcipher.dll改名成sqlite3.dll,然后将改名后的sqlite3.dll复制替换C:\Program Files\PremiumSoft\Navicat Premium 16目录下的sqlite3.dll即可

接下来新建SQLite 3的连接

image

并在高级中填写密码

image

就可以打开了。

image

参考

标签:Core,https,跨平台,context,var,new,Net,public
From: https://www.cnblogs.com/taylorshi/p/16861628.html

相关文章