使用bundle_e_sqlcipher和SqlSugarNoDrive的bug 返回

SqlSugar 沟通中
17 422
该叫什么 鼠赽 发布于2周前
悬赏:0 飞吻


没有找到SqlSugarNoDrive的源码,感觉是在SplitTable的时候创建SqliteConnection的时候没有使用configAction


引用的库是:


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

	<PropertyGroup>
		<OutputType>Exe</OutputType>
		<TargetFramework>net6.0</TargetFramework>
		<ImplicitUsings>enable</ImplicitUsings>
		<Nullable>enable</Nullable>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.1" />
		<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.10" />
		<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.175" />
	</ItemGroup>
</Project>


代码是:


using SqlSugar;
var connStr = @"Data Source=z.db;";
var _writeDB = new SqlSugarClient(new ConnectionConfig()
{
    ConnectionString = connStr,
    DbType = DbType.Sqlite,
},
db =>
{
    db.Ado.ExecuteCommand($"pragma key='abcde';");
    db.Ado.ExecuteCommand($"PRAGMA cipher_compatibility = 3;");
}
);
_writeDB.CodeFirst.InitTables<SplitRecord>();
var id = new Random().Next();

_writeDB.Insertable(new SplitRecord() { ID = id }).SplitTable().ExecuteCommand();//这句话报错了

var ret = _writeDB.Queryable<SplitRecord>().SplitTable().ToList();
Console.WriteLine(ret.Count);
Console.Read();
[SplitTable(SplitType.Month)]
[SugarTable("AlarmRecord_{year}{month}{day}")]
public class SplitRecord
{
    public int ID { set; get; }
    [SplitField]
    public DateTime Time { get; } = DateTime.Now;
}


报错:


Microsoft.Data.Sqlite.SqliteException:“SQLite Error 26: 'file is not a database'.”


热忱回答17

  • 感觉应该是SqlSugarProvider.CopyNew的问题,这里没有使用configAction

            public SqlSugarClient CopyNew()
            {
                var result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig));
                result.QueryFilter = this.QueryFilter;
                return result;
            }


    0 回复
  • https://www.donet5.com/Home/Doc?typeId=2366

    你上面代码并没用到CopyNew(), 按模版提供完整的demo

    0 回复
  • using SqlSugar;
    namespace Test
    {
        internal class Program
        {
            static void Main(string[] args)
            {
                //创建连接
                var connStr = @"Data Source=z.db;";
                var _writeDB = new SqlSugarClient(new ConnectionConfig()
                {
                    ConnectionString = connStr,
                    DbType = DbType.Sqlite,
                    IsAutoCloseConnection = false
                },
                db =>
                {
                    //在这里初始化1是不想把密码暴露在ConnectionString里,2是希望能用cipher_compatibility=3
                    db.Ado.ExecuteCommand("pragma key='abcde';");
                    db.Ado.ExecuteCommand("PRAGMA cipher_compatibility = 3;");
                }
                );
                
                var id = new Random().Next();
                //建表
                _writeDB.CodeFirst.InitTables<SplitRecord>();
                //插入数据,这一句出错
                _writeDB.Insertable(new SplitRecord() { ID = id }).SplitTable().ExecuteCommand();
                var ret = _writeDB.Queryable<SplitRecord>().SplitTable().ToList();
                Console.WriteLine(ret.Count);
                Console.Read();
            }
            
            //测试类,主要是SplitTable会引起问题
            [SplitTable(SplitType.Month)]
            [SugarTable("AlarmRecord_{year}{month}{day}")]
            public class SplitRecord
            {
                public int ID { set; get; }
                [SplitField]
                public DateTime Time { get; } = DateTime.Now;
            }
        }
    }


    实在不会改成黑底白字了。。。


    以下都是我的猜测:


    我开了SqlSugar的源码,也会出现这个问题。读源码可得:

    _writeDB.Insertable(new SplitRecord() { ID = id }).SplitTable().ExecuteCommand();

    中,会调用 

    SplitInsertable<T>.ExecuteCommand()

    =>SplitInsertable<T>.CreateTable()

    =>var newDb = this.Context.CopyNew();

    跟踪线程的时候发现这里的 Context是SqlSugar.SqlSugarProvider

    它的CopyNew是

            public SqlSugarClient CopyNew()
            {
                var result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig));
                result.QueryFilter = this.QueryFilter;
                return result;
            }

    这里直接通过ConnectionString创建,就不会经过SqlSugarClient的configAction

    导致不会执行

    db.Ado.ExecuteCommand("pragma key='abcde';");
    db.Ado.ExecuteCommand("PRAGMA cipher_compatibility = 3;");

    这个新建的connection没有密码,所以在读写数据库的时候就会报错

    Microsoft.Data.Sqlite.SqliteException:“SQLite Error 26: 'file is not a database'.”

    0 回复
  • image.png

    你提供代码是可以跑的 ,我这边测试通过了,还有问题将能重现的DEMO删掉OBJ和BIN打包上传

    0 回复
  • image.png


    SugarTest.rar


    哎呀放假了~没看~

    我这里总是出错

    0 回复
  • 应该是执行这个东西导致当前链接找不到库

     db.Ado.ExecuteCommand("pragma key='abcde';");

     db.Ado.ExecuteCommand("PRAGMA cipher_compatibility = 3;");



    只要执行这2话就不行

    image.png

    0 回复
  • @fate sta

    这两行是sqlcipher用来设置密码和加密版本的,应该在连接的第一时间使用

    我尝试在SqlSugar.SqlSugarProvider.CopyNew()中手动添加这两行,就不会有这个错误了


    如果加密的数据库不用这两行直接读取,就会出现‘file is not a database'



    0 回复
  • @fate sta

    总之就是SqlSugarClient的configAction,没有应用到某些copyNew里

    主要出错的场景就是:

    密码不是直接显示在ConnectionString而是对链接进行一些处理,此时用一些使用到copynew比如splittable中的一些操作,就会出错

    0 回复
  • SqlSugar.SqlSugarProvider.CopyNew() 等于没有生效,是不同的db连接

    0 回复
  • 这样吧,你用ado.net写一个CRUD 示例能用的,我这边用sqlsugar按你要求进行测试

    0 回复
  • sqlitetest.zip

    static void Main(string[] args)
    {
    var connStr = @"Data Source=z.db;";
                //创建数据库,用SqlCipher加密方式,所以这个数据库用sqlite studio之类的是打不开的
    var fi = new FileInfo("z.db");
    if (fi.Exists)
    {
                    // fi.Delete();
    }
    using (var conn = CreateByConnStr(connStr))
    {
    CreateTable(conn);
    conn.Close();
    }
    SqliteConnection.ClearAllPools();
    
                //测试使用connectionstring方式加密,没问题
    using (var conn = CreateByConnStr(connStr))
    {
    Console.WriteLine("test by connectionstring");
    TestConn(conn);
    conn.Close();
    }
    SqliteConnection.ClearAllPools();
    
                //测试使用命令的加密,没问题
    using (var conn = CreateByAdo(connStr))
    {
    Console.WriteLine("test by ado");
    TestConn(conn);
    conn.Close();
    }
    SqliteConnection.ClearAllPools();
    
                //测试不加密连接,显示file is not a database
    using (var conn = CreateSurceConn(connStr))
    {
    Console.WriteLine("test by source");
    TestConn(conn);
    conn.Close();
    }
    
    Console.WriteLine("end");
    }


    0 回复
  • @鼠赽

    着实是不太会用网页编辑,而且手头就一mac也不方便~源项目附上了


    主要还是用sqlcipher加密数据库的话是有两种方式的(另外就是加密格式cipher_compatibility = 3好像只能用pragma设置)

    1、密码填在ConnectionString里

    2、在刚创建连接的时候pragma key=‘abcde’;


    用第一种方式的话,这个密码是明文可见的,所以我比较希望能用第二种


    如果用没有密码的connectionstring创建SqliteConnection的话,执行pramga key=‘abcde';就可以正常使用加密后的db文件

    如果不执行的话,命令就会显示 file is not a database


    大概应该就是这个情况

    0 回复
  • db.ado.Connection=CreateByConnStr(connStr)这样就行,自动释放关闭用手动释放

    0 回复
  • sqlsugar可以直接用ado.net的connection

    0 回复
  • 你拿上面的DEMO小改一下就能用了

    0 回复
  • @fate sta

    主要是不想用ConnStr的方式,这种方式的SqlSugarClient可以直接从ConnStr中看到密码值。

    想用 pramga key=‘abcde' 的方式实现。。。


    而如果用pragma key="abcde"的方式手动创建SqliteConnection的话,在SplitTable中还是会用到SqlSugar.SqlSugarProvider.CopyNew(),通过ConnStr创建新连接

    依然会出现这个错误

    0 回复
  • @fate sta

    归纳一下:

    我的主要目的是通过Execute("pragma key='abcde'")的方式实现加密

    这样密码就不会体现在ConnectionString中(另外还有个小需求就是需要"PRAGMA cipher_compatibility = 3;")


    而在SplitTable的一系列操作中,会通过ConnectionString生成一个新的连接用来处理CreateTable等操作(SqlSugar.SqlSugarProvider.CopyNew())

    而这个新的db由于没有执行 pragma key='abcde' 会导致 file is not a database 错误

    0 回复