我正在使用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,所以也许我不得不做一些难看的硬编码....
还有其他想法或建议吗?
答案 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);
}
}