使用 SubSonic 和 sqlite 插入主键值 - 问题是因为 SubSonic 认为列要么是 AutoIncrement,要么不是

StackOverflow https://stackoverflow.com/questions/2283063

我正在使用 SubSonic 2.2 和 sqlite,并且在处理具有非自动增量的 INTEGER PRIMARY KEY 列的表时遇到了问题。根据 常问问题:

如果将表的某一列声明为 INTEGER PRIMARY KEY,则每当您将 NULL 插入表的该列时,NULL 都会自动转换为整数,该整数比该列相对于所有其他行的最大值大 1在表中,如果表为空,则为 1。

所以 sqlite 认为这些列是 有时 自动递增(即仅当提供 NULL 值时)。问题是 SubSonic 认为他们是 总是 自动递增。

在我的应用程序中,我的 ID 值是从远程数据库生成的,因此我不想在 sqlite 中自动生成它们。这应该没问题:当我在此表中创建记录时,我将简单地提供值。但是,当我使用 SubSonic 的 sonic.exe 自动生成 DAL 时,主键列设置为 AutoIncrement = true。这似乎意味着我无法设置 ID 列 - subsonic 的 ActiveHelper.GetInsertCommand() 会忽略它,因为它认为它是自动生成的。

确定是否自动增量的行位于 SubSonic.SQLiteDataProvider.GetTableSchema() 中:

column.AutoIncrement = Convert.ToBoolean(row["PRIMARY_KEY"]) && GetDbType(row["DATA_TYPE"].ToString()) == DbType.Int64;

我想解决方案是

  • 不使用 INTEGER PRIMARY KEY 列作为在其他地方生成的键,或者

  • 修改模板,以便这些类型的列不会设置为 AutoIncrement = true。这意味着 SubSonic 永远不会将它们视为自动增量,因此我需要小心,不要稍后期望获得自动生成的值。不幸的是,我认为在模板中不可能轻松确定该列是否真的是自动增量,所以也许我必须做一些丑陋的硬编码......

还有其他想法或建议吗?

有帮助吗?

解决方案

不幸的是,看起来我们的 SQLiteDataProvider 假设如果它是 Int64 PK,那么它是自动递增的。我现在正在查看源代码(我没有编写此提供程序),我可以看到加载架构的方式是使用 Connection.GetSchema - 它使用内置的 System.Data.Common.DbConnection 来获取架构对于桌子。

大多数时候这不是最佳的,因为它返回的信息有限。在这种情况下 - 它没有告诉我们该列是否是自动增量的。可能有更好的方法来询问 SQLite 表上的元信息 - 但不幸的是它没有被使用。

简短回答:如果可以的话,定义一个新的 PK,并使用另一个密钥作为参考。

其他提示

正如我之前提到的,我在 12 月签入了修订后的 SQLiteDataProvider。检查 SQLiteDataProvider.cs 中的第 407 行是否有:

// 自动增量检测现在在最近的 System.Data.SQLite 中可用。1.0.60.0 -- 保罗 列。AutoIncrement = Convert.ToBoolean(row[“AUTOINCREMENT”]);

周围的线路还进行了一些其他改进和错误修复。我想,新代码从未添加到 github 中的主项目发行版中,我不太关注该项目。除了文件级锁定之外,SQLite 也是一个出色的提供者。我有一个 System.Data.SQLite 的自制版本,它使用 SQLite 的新外键功能,正式版本应该在本月完成?

这是修改后的版本:SQLiteDataProvider.cs

顺便说一句,如果您需要从 sql server 进行转换,请查看此项目:

将 SQL Server 数据库转换为 SQLite 数据库http://www.codeproject.com/KB/database/convsqlservertosqlite.aspx

我发现由于文件锁定,我无法使用 SqlDataProvider 中编写的 CreateConnection 。SQLiteDataProvider 中的 CreateConnection 现在是错误的,因为它忽略新的连接字符串。

System.Data.SQLite 文档说“您可以创建多个线程,这些线程可以创建自己的 SQLiteConnection 和后续对象来访问数据库。多个线程上与同一数据库文件的多个连接是完全可以接受的,并且行为是可预测的。”

所以我尝试过以下内容,而且确实很笨拙。使用以线程 ID 和连接字符串为键的连接字典。但所有单元测试都通过了,包括大多数交易(需要更好的测试)。我编写了更多带有关键部分锁的事务测试,我认为它可能是线程安全的,只需要更实际的测试。

private Dictionary<string, SQLiteConnection> threadConnectionTable = new Dictionary<string, SQLiteConnection>();

public override DbConnection CreateConnection(string newConnectionString)
{
    SQLiteConnection conn;
    string connKey = "t" + Thread.CurrentThread.ManagedThreadId + "__" + newConnectionString;
    if(threadConnectionTable.ContainsKey(connKey))
    {
        conn = threadConnectionTable[connKey];
        if(conn.State != ConnectionState.Open)
            conn.Open();
        return conn;
    }
    conn = new SQLiteConnection(newConnectionString);
    conn.Open();
    threadConnectionTable[connKey] = conn;
    return conn;
}



private Object thisLock = new Object();

[Test]
[ThreadedRepeat(10)]
public void MultiThreadRepeat()
{
    lock(thisLock)
    {
        var qcc = new QueryCommandCollection();
        int threadId = Thread.CurrentThread.ManagedThreadId;
        Debug.WriteLine("MultiThreadRepeat: thread id = " + threadId);
        int count = 0;
        for(int n = 0; n < 10; n++)
        {
            Query qry1 = new Query(Product.Schema);
            qry1.QueryType = QueryType.Update;
            qry1.AddWhere(Product.Columns.ProductID, n);
            qry1.AddUpdateSetting("ProductName", threadId + ": unit test ");
            QueryCommand cmd = qry1.BuildUpdateCommand();
            qcc.Add(cmd);
            count++;
        }
        DataService.ExecuteTransaction(qcc);
        var p1 = new Product(1);
        Assert.AreEqual(p1.ProductName, threadId + ": unit test ", StringComparison.InvariantCultureIgnoreCase);
    }

}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top