Вставка значений первичного ключа с использованием SubSonic и sqlite - проблема, потому что SubSonic считает, что столбцы являются либо AutoIncrement, либо нет - PullRequest
1 голос
/ 17 февраля 2010

Я использую SubSonic 2.2 и sqlite и столкнулся с проблемой при работе с таблицами со столбцом INTEGER PRIMARY KEY, который не является AUTOINCREMENT. Согласно faq :

Если вы объявляете столбец таблицы INTEGER PRIMARY KEY, то всякий раз, когда вы вставляете NULL в этот столбец таблицы, NULL автоматически преобразуется в целое число, которое на единицу больше, чем наибольшее значение этого столбца над все остальные строки таблицы или 1, если таблица пуста.

Таким образом, sqlite считает, что эти столбцы иногда автоматически увеличиваются (т. Е. Только когда предоставляются значения NULL). Проблема в том, что SubSonic считает, что они всегда с автоматическим приращением.

В моем приложении мои значения идентификаторов генерируются из удаленной базы данных, поэтому я не хочу автоматически генерировать их в sqlite. Это не должно быть проблемой: я просто предоставлю значения при создании записей в этой таблице. Однако, когда я использую файл sonic.exe от SubSonic для автоматической генерации моего DAL, для столбца первичного ключа устанавливается значение AutoIncrement = true. Похоже, это означает, что я не могу установить столбец идентификатора - ActiveHelper в subsonic. 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 ]

2 голосов
/ 17 февраля 2010

К сожалению, похоже, что наш SQLiteDataProvider предполагает, что если это Int64 PK, то это автоинкремент. Я сейчас просматриваю источник (я не писал этого провайдера) и вижу, что при загрузке схемы используется Connection.GetSchema, который использует встроенную System.Data.Common.DbConnection для получения схемы для столов.

Это в большинстве случаев неоптимально, поскольку возвращает ограниченную информацию. В этом случае - это не говорит нам, является ли колонка AUTOINCREMENT или нет. Возможно, есть лучший способ запросить у мета-информации в таблице SQLite, но, к сожалению, она не использовалась.

Краткий ответ: определите новый PK, если можете, и используйте другую клавишу в качестве ссылки.

1 голос
/ 20 февраля 2010

Как я уже упоминал ранее, в декабре я проверил исправленный SQLiteDataProvider. Проверьте, что в строке 407 в SQLiteDataProvider.cs у вас есть:

// Обнаружение автоинкремента теперь доступно в недавнем System.Data.SQLite. 1.0.60.0 - Пол column.AutoIncrement = Convert.ToBoolean (row ["AUTOINCREMENT"]);

Есть также несколько других улучшений и исправлений ошибок в соседних строках. Думаю, новый код никогда не добавлялся в основной дистрибутив проекта в github. Я не слишком много слежу за проектом. SQLite был замечательным провайдером, кроме блокировки на уровне файлов. У меня есть собственная версия System.Data.SQLite, в которой используются новые функции внешних ключей SQLite, и официальная версия должна быть сделана в этом месяце?

Вот пересмотренная версия: SQLiteDataProvider.cs

Кстати, проверьте этот проект на случай, если вам нужно конвертировать с сервера sql:

Преобразование БД SQL Server в БД SQLite http://www.codeproject.com/KB/database/convsqlservertosqlite.aspx

0 голосов
/ 22 февраля 2010

Я обнаружил, что не могу использовать CreateConnection, написанный как в SqlDataProvider, из-за блокировки файла. CreateConnection в SQLiteDataProvider, как сейчас, является неправильным, поскольку он игнорирует новые строки подключения.

Документ System.Data.SQLite гласит: «Вы можете создать несколько потоков, и эти потоки могут создавать свои собственные объекты SQLiteConnection и последующие объекты для доступа к базе данных. Несколько соединений в нескольких потоках с одним файлом базы данных вполне приемлемы и будут работать предсказуемо. "

Итак, я попробовал следующее, и это действительно грязно. Используйте словарь соединений с ключом идентификатора потока и строкой соединения. Но все модульные тесты пройдены, включая большинство транзакций (требуются более качественные тесты). Я написал еще пару тестов транзакций с блокировками критических секций и думаю, что они могут быть поточно-ориентированными, просто нужно более реалистичные тесты.

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

}
...