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

发布于 2024-08-22 10:48:59 字数 1104 浏览 7 评论 0原文

我正在使用 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 永远不会将它们视为自动增量,因此我需要小心,不要稍后期望获得自动生成的值。不幸的是,我认为不可能在模板中轻松确定该列是否真的是自动增量,所以也许我必须做一些丑陋的硬编码......

还有其他想法或建议吗?

I'm using SubSonic 2.2 and sqlite and have encountered a problem when dealing with tables with an INTEGER PRIMARY KEY column that isn't AUTOINCREMENT. According to the faq:

If you declare a column of a table to be INTEGER PRIMARY KEY, then whenever you insert a NULL into that column of the table, the NULL is automatically converted into an integer which is one greater than the largest value of that column over all other rows in the table, or 1 if the table is empty.

So sqlite thinks these columns are sometimes auto incremented (ie just when NULL values are provided). The problem is that SubSonic thinks they are always auto incremented.

In my application my ID values are generated from a remote database, so I don't want to auto-generate them in sqlite. This should be no problem: I'll simply provide values when I create records in this table. However, when I use SubSonic's sonic.exe to auto-generate my DAL, the primary key column is set to AutoIncrement = true. This seems to mean that I can't set the ID column - subsonic's ActiveHelper.GetInsertCommand() ignores it, since it thinks it's auto-generated.

The line where it determines whether it's autoincrement or not is in SubSonic.SQLiteDataProvider.GetTableSchema():

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

I guess the solution is either to

  • Not use INTEGER PRIMARY KEY columns for keys that are generated elsewhere, or

  • Modify the templates so these sorts of columns are not set to AutoIncrement = true. This would mean SubSonic won't ever treat them as auto increment, so I'd need to be careful that I don't later expect to get auto generated values. Unfortunately I don't think it's possible within the templates to easily determine if the column really is AUTOINCREMENT or not, so maybe I'd have to do some ugly hard-coding instead....

Any other thoughts or suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

若无相欠,怎会相见 2024-08-29 10:48:59

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

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

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

Unfortunately it looks like our SQLiteDataProvider assumes that if it's an Int64 PK, then it's auto-increment. I'm looking over the source right now (I didn't write this provider) and I can see that the way the schema is being loaded is using Connection.GetSchema - which uses built System.Data.Common.DbConnection to get the schema for the tables.

This is suboptimal most of the time because it returns limited information. In this case - it's not telling us if the column is AUTOINCREMENT or not. There's probably a better way to ask SQLite the meta information on the table - but it wasn't used unfortunately.

Short answer: define a new PK, if you can, and use the other key as a reference.

一个人的旅程 2024-08-29 10:48:59

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

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

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

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

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

Convert SQL Server DB to SQLite DB
http://www.codeproject.com/KB/database/convsqlservertosqlite.aspx

As I mentioned before I checked in a revised SQLiteDataProvider in december. Check that at line 407 in SQLiteDataProvider.cs you have:

// Autoincrement detection now available in recent System.Data.SQLite. 1.0.60.0 -- paul
column.AutoIncrement = Convert.ToBoolean(row["AUTOINCREMENT"]);

There are also several other improvements and bug fixes in the surrounding lines. The new code was never added to the main project distribution in github I guess, I don't follow the project too much. SQLite has been a wonderful provider aside from the file level locking. I have a home grown version of System.Data.SQLite that uses the new foreign key features of SQLite, and the official version should be done this month?

Here is the revised version:
SQLiteDataProvider.cs

BTW, check out this project in case you need to convert from sql server:

Convert SQL Server DB to SQLite DB
http://www.codeproject.com/KB/database/convsqlservertosqlite.aspx

过期情话 2024-08-29 10:48:59

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

}

I find I can't use a CreateConnection written as in SqlDataProvider because of the file locking. The CreateConnection in SQLiteDataProvider as it is now is wrong since it ignores new connection strings.

The System.Data.SQLite doc says "You May create multiple threads, and those threads can create their own SQLiteConnection and subsequent objects for accessing a database. Multiple connections on multiple threads to the same database file are perfectly acceptable and will behave predictably."

So what I have tried is the following, and it's really cludgey. Use a dictionary of connections keyed by thread id and connection string. But all of the unit tests pass, including most of the transactions (needs better tests). I wrote a couple more transaction tests, with critical section locks, and I think it might be thread safe, just need more realistic tests.

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);
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文