1

我正在使用 SubSonic 2.2 和 sqlite,在处理具有非 AUTOINCREMENT 的 INTEGER PRIMARY KEY 列的表时遇到了问题。根据常见问题解答

如果您将表的一列声明为 INTEGER PRIMARY KEY,那么每当您在表的该列中插入 NULL 时,NULL 都会自动转换为一个整数,该整数比该列在所有其他行中的最大值大一在表中,如果表为空,则为 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 永远不会将它们视为自动增量,因此我需要小心我以后不会期望获得自动生成的值。不幸的是,我认为在模板中无法轻松确定该列是否真的是 AUTOINCREMENT,所以也许我不得不做一些难看的硬编码......

还有其他想法或建议吗?

4

3 回答 3

2

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

这在大多数情况下都不是最理想的,因为它返回的信息有限。在这种情况下 - 它不会告诉我们该列是否为 AUTOINCREMENT。可能有更好的方法来向 SQLite 询问表上的元信息——但不幸的是它没有被使用。

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

于 2010-02-17T18:54:12.277 回答
1

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

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

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

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

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

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

于 2010-02-20T09:06:04.527 回答
0

我发现由于文件锁定,我无法使用在 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);
    }

}
于 2010-02-22T09:12:32.430 回答