【问题标题】:Inserting primary key values using SubSonic and sqlite - problem because SubSonic thinks columns are either AutoIncrement or not使用 SubSonic 和 sqlite 插入主键值 - 问题因为 SubSonic 认为列是 AutoIncrement 或不是
【发布时间】:2011-01-17 23:47:09
【问题描述】:

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

如果您将表的列声明为 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 永远不会将它们视为自动增量,因此我需要小心我以后不会期望获得自动生成的值。不幸的是,我认为无法在模板中轻松确定该列是否真的是 AUTOINCREMENT,所以也许我不得不做一些难看的硬编码......

还有其他想法或建议吗?

【问题讨论】:

    标签: .net sqlite subsonic subsonic2.2


    【解决方案1】:

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

    【讨论】:

    • 为什么不像 SQLServer 和 MySql 提供程序那样每次都创建新连接?
    • 这是我尝试的第一件事,但结果每次都是锁定文件错误。不知道问题出在哪里,能解决吗?以后有时间我会做实验。
    • 我的问题是我正在使用 subsonic2 单元测试,他们正在显示这些问题。由于某种原因,在事务之后运行的数据读取器因锁定错误而失败。到目前为止,我无法在我自己的不使用亚音速的测试应用程序中失败。
    • 这段代码看起来不错,我会尝试使用它。顺便说一句,我认为这个答案真的是针对我的另一个问题:stackoverflow.com/questions/2291364 所以也许把它移到那里?
    • 我现在记得,我使用旧的 subsonic 源代码控制检查了 sqlite 单元测试(现已消失)。我没有将它们签入 github,只有提供者 .cs 文件。我可以在不久的将来将单元测试签入 github。我发现将单元测试复制到 subsonic 源中的新项目更容易,然后根据 sqlite 的需要对其进行修改,编辑 app.config 等。然后必须添加一个新的 northwind.sqlite 数据库文件。我必须重新学习如何 github,然后希望 Rob 允许签入所有这些。
    【解决方案2】:

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

    // 自动增量检测现在在最近的 System.Data.SQLite 中可用。 1.0.60.0——保罗 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

    【讨论】:

    • 酷,谢谢。您知道它仍在使用 System.Data.SQLite 的 1.0.60.0 版本而不是 1.0.65.0 的任何原因吗?
    • @Paul,感谢您更新的 SQLiteDataProvider.cs - 结合 System.Data.SQLite v1.0.65.0 它似乎有助于解决我遇到的一些多线程锁定问题,但仍然存在问题.我认为 SQLiteDataProvider 不应使用与数据库的单个连接。如果提供程序随后被多个线程使用,则会导致问题。 Rob 说 (sqlite.phxsoftware.com/forums/p/56/373.aspx) “如果多个线程必须与同一个数据库文件通信,那么就建立多个连接并确保不共享它们。”。
    • 我遇到的问题是 DataService.ExecuteTransaction 上的 SQLiteException,当我有多个线程同时调用 DataService.ExecuteTransaction() 时,消息“库使用不正确没有事务在此连接上处于活动状态”时间。
    • PS:我知道 SQLiteDataProvider 在您更改之前共享数据库连接 - 我只是想您可能知道它是否真的应该像这样......
    • 我不知道 subsonic 发行版中 System.Data.SQLite 的版本,我不跟踪它。它可能应该始终更新到最新版本。如果您可以修复多个连接的提供程序,请这样做,并将修改后的代码检查到 github 项目中。当我试图让所有单元测试通过时,我当然可能犯了错误。
    【解决方案3】:

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

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

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

    【讨论】:

    • 谢谢,是的,我认为这就是我要采用的解决方案,不幸的是,我在很多地方都使用 FetchById,因此必须更改这样的调用才能以不同的方式查找记录。
    猜你喜欢
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多