EF Core 访问加密Sqlite异常解决方法

最近打算升级到EF Core ,数据库是Sqlite,找了一个案例,按步就班:

    public class SampleDBContext : DbContext

    {
        private SqliteConnection m_conn = null;
        public SqliteConnection Conn
        {
            get
            {
                if (m_conn == null)
                {
                    SQLitePCL.Batteries.Init();
                    string dbName=Path.Combine(Environment.CurrentDirectory, "SampleDB.db");
                    string connStr=new SqliteConnectionStringBuilder(){
                        DataSource= dbName,
                        Mode=SqliteOpenMode.ReadWriteCreate
                        ,Password ="admin"
                    }.ToString();
                    m_conn = new SqliteConnection(connStr);
                    m_conn.Open();
                }
                return m_conn;
            }
        }
        private static bool _created = false;
        public SampleDBContext()
        {
            if (!_created)
            {
                _created = true;
                Database.EnsureDeleted();
                Database.EnsureCreated();
            }
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionbuilder)
        {
            optionbuilder.UseSqlite(Conn);
        }
    }

运行后出现如下错误:

图1
Method not found: 'Int32 SQLitePCL.ISQLite3Provider.sqlite3_win32_set_directory(Int32, System.String)"

Method not found: 'Int32 SQLitePCL.ISQLite3Provider.sqlite3_win32_set_directory(Int32, System.String)"

注释掉SQLitePCL.Batteries.Init();问题依旧

图2

着重参考了下列两篇文章

https://www.bricelam.net/2016/06/13/sqlite-encryption.html

https://github.com/ericsink/SQLitePCL.raw/issues/290

折腾了好久,结果问题依旧,放弃吧......

不甘心,过了两天,重拾旧业,仔仔细细,反复查看,发现SQLitePCLRaw.bundle_sqlcipher(1.1.14)默认引用的SQLitePCLRaw.core是2.02版本

图3

而最新的SQLitePCLRaw.core是2.03版本,因此试着对包SQLitePCLRaw.provider.sqlcipher(2.03)及SQLitePCLRaw.core(2.03)进行显式引用,,结果...

图4

是新问题,然后,注释掉Database.EnsureDeleted(),再次编译,运行,终于...

图5

大功告成!

最后把对SQLitePCLRaw.core的显示引用去除,项目文件内容如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="3.1.4" />
    <PackageReference Include="SQLitePCLRaw.bundle_sqlcipher" Version="1.1.14" />
    <PackageReference Include="SQLitePCLRaw.provider.sqlcipher" Version="2.0.3" />
  </ItemGroup>
</Project>

解决新问题:

最后一个问题的出现,分析了一下原因,提示写着,数据库被其他进程占用了,通过跟踪发现其实并不是被其他进程占用了,而是调用Database.EnsureDeleted()时,连接已经打开了,optionbuilder.UseSqlite使用了SqliteConnection,把optionbuilder.UseSqlite的参数改为连接字符串,问题就消失了。

最终程序如下:

using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System;
using System.IO;
using System.Linq;

namespace EFCoreTest
{
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class SampleDBContext : DbContext
    {
        private SqliteConnection m_conn = null;
        public SqliteConnection Conn
        {
            get
            {
                if (m_conn == null)
                {
                    //SQLitePCL.Batteries.Init();
                    //string dbName=Path.Combine(Environment.CurrentDirectory, "SampleDB.db");
                    //string connStr=new SqliteConnectionStringBuilder(){
                    //    DataSource= dbName,
                    //    Mode=SqliteOpenMode.ReadWriteCreate
                    //    ,Password ="admin"
                    //}.ToString();
                    m_conn = new SqliteConnection(ConnStr);
                    m_conn.Open();
                }
                return m_conn;
            }
        }
        private string ConnStr
        {
            get
            {
                string dbName = Path.Combine(Environment.CurrentDirectory, "SampleDB.db");
                return new SqliteConnectionStringBuilder()
                {
                    DataSource = dbName,
                    Mode = SqliteOpenMode.ReadWriteCreate,
                    Password = "admin"
                }.ToString();
            }
        }
        private static bool _created = false;
        public SampleDBContext()
        {
            if (!_created)
            {
                _created = true;
                Database.EnsureDeleted();
                Database.EnsureCreated();
            }
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Category>().ToTable("tb_Category");
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionbuilder)
        {
            optionbuilder.UseSqlite(ConnStr);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //Console.WriteLine("Hello World!");

            using (var dbContext = new SampleDBContext())
            {
                dbContext.Set<Category>().Add(new Category { Name = "Wigs" });
                dbContext.Set<Category>().Add(new Category { Name = "Shoes" });
                dbContext.Set<Category>().Add(new Category { Name = "Dresses" });
                dbContext.SaveChanges();

                foreach (var cat in dbContext.Set<Category>().ToList())                 {                     Console.WriteLine($"CategoryId= {cat.Id}, CategoryName = {cat.Name}");                 }             }             Console.ReadKey();         }     } }