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

时间:2010-02-17 17:47:09

标签: .net sqlite subsonic subsonic2.2

我正在使用SubSonic 2.2和sqlite,并且在处理具有非AUTOINCREMENT的INTEGER PRIMARY KEY列的表时遇到了问题。根据{{​​3}}:

  

如果将一个表的列声明为INTEGER PRIMARY KEY,那么无论何时在表的该列中插入NULL,NULL都会自动转换为一个整数,该整数大于该列的最大值。表中的所有其他行,如果表为空,则为1。

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

在我的应用程序中,我的ID值是从远程数据库生成的,所以我不想在sqlite中自动生成它们。这应该没问题:我在这个表中创建记录时只提供值。但是,当我使用SubSonic的sonic.exe自动生成我的DAL时,主键列设置为AutoIncrement = true。这似乎意味着我无法设置ID列 - 亚音速的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,所以也许我不得不做一些难看的硬编码....

还有其他想法或建议吗?

3 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

正如我之前提到的,我在12月份检查了一个修改过的SQLiteDataProvider。检查SQLiteDataProvider.cs中第407行是否有:

//最近在System.Data.SQLite中提供了自动增量检测功能。 1.0.60.0 - 保罗 column.AutoIncrement = Convert.ToBoolean(row [“AUTOINCREMENT”]);

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

以下是修订版: SQLiteDataProvider.cs

BTW,如果您需要从sql server转换,请查看此项目:

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

答案 2 :(得分:0)

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

System.Data.SQLite doc说“你可以创建多个线程,这些线程可以创建自己的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);
    }

}