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

Я использую 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.GetInsertCommand () subsonic игнорирует его, поскольку считает, что он создается автоматически.

Строка, в которой определяется, есть ли автоинкремент или нет, находится в SubSonic.SQLiteDataProvider.GetTableSchema ():

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

Думаю, решение - либо

  • Не использовать столбцы INTEGER PRIMARY KEY для ключей, сгенерированных в другом месте, или

  • Измените шаблоны так, чтобы для этих типов столбцов не было установлено значение AutoIncrement = true. Это означало бы, что SubSonic никогда не будет рассматривать их как автоматическое приращение, поэтому мне нужно быть осторожным, чтобы позже я не ожидал получить автоматически сгенерированные значения. К сожалению, я не думаю, что с помощью шаблонов можно легко определить, действительно ли столбец является AUTOINCREMENT или нет, поэтому, возможно, мне придется вместо этого сделать некрасивое жесткое кодирование ...

Есть другие мысли или предложения?


person Rory    schedule 17.02.2010    source источник


Ответы (3)


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

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

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

person Community    schedule 17.02.2010
comment
спасибо, да, я думаю, что это решение, которое я выберу, к сожалению, я использую FetchById во многих местах, поэтому мне придется изменить такие вызовы, чтобы по-другому искать записи. - person Rory; 17.02.2010

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

// Обнаружение автоинкремента теперь доступно в последней версии System.Data.SQLite. 1.0.60.0 - столбец paul.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

person P a u l    schedule 20.02.2010
comment
Круто, спасибо за это. Знаете ли вы, почему он все еще использует версию 1.0.60.0 System.Data.SQLite вместо 1.0.65.0? - person Rory; 22.02.2010
comment
@Paul, спасибо за ваш обновленный SQLiteDataProvider.cs - в сочетании с System.Data.SQLite v1.0.65.0 он, кажется, помогает с некоторыми проблемами многопоточной блокировки, которые у меня возникают, но проблема все еще существует. Мне кажется, что SQLiteDataProvider не должен использовать одно соединение с базой данных. Если поставщик затем используется несколькими потоками, это вызовет проблемы. Роб говорит (sqlite.phxsoftware.com/forums/p/56/373.aspx) Если несколько потоков должны взаимодействовать с одним и тем же файлом базы данных, сделайте несколько подключений к нему и убедитесь, что вы не делитесь ими. - person Rory; 22.02.2010
comment
Проблема, которую я получаю, - это SQLiteException в DataService.ExecuteTransaction, библиотека сообщений используется неправильно. В этом соединении не активна транзакция, когда у меня есть несколько потоков, вызывающих DataService.ExecuteTransaction () одновременно. - person Rory; 22.02.2010
comment
PS: Я знаю общие подключения к базе данных SQLiteDataProvider до ваших изменений - я просто подумал, что вы можете знать, действительно ли это должно быть так ... - person Rory; 22.02.2010
comment
Я не знаю версию System.Data.SQLite, которая находится в раздаче subsonic, я ее не отслеживаю. Вероятно, его всегда следует обновлять до последней версии. Если вы можете исправить поставщика для нескольких подключений, сделайте это и проверьте исправленный код в проекте github. Я, конечно, мог сделать ошибки, когда возился, пытаясь пройти все модульные тесты. - person P a u l; 22.02.2010
comment
Я бы восстановил код, связанный с подключениями, таким, каким он был до того, как я его изменил, это всего несколько строк, а затем запустил бы тесты на нем. Мы должны сделать так, как говорит Симпсон, при повторном создании потоков. Это должно быть легко проверить в течение нескольких минут. - person P a u l; 22.02.2010
comment
Я запустил модульные тесты с использованием старого провайдера, и ничего не работает из-за ошибки «файл базы данных заблокирован». Вы попали в самую дефектную часть проекта, и это нужно исправить. У меня сложилось впечатление, что потокобезопасность невозможна из-за способа, которым провайдер управляет соединением. - person P a u l; 22.02.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);
    }

}
person P a u l    schedule 22.02.2010
comment
Почему бы просто не создавать каждый раз новые подключения, как поставщики SQLServer и MySql? - person Rory; 22.02.2010
comment
Это первое, что я пробовал, но в результате каждый раз возникает ошибка заблокированного файла. Не знаю точно, в чем проблема, может, вы сможете исправить? Я собираюсь поэкспериментировать позже, так как у меня будет время. - person P a u l; 22.02.2010
comment
Моя проблема в том, что я работаю с модульными тестами subsonic2, и они показывают эти проблемы. По какой-то причине программа чтения данных, запущенная после транзакции, выдает ошибку блокировки. Пока я не могу сделать это в моем собственном тестовом приложении, которое не использует дозвуковой режим. - person P a u l; 23.02.2010
comment
этот код выглядит хорошо, я попробую его использовать. Кстати, я думаю, что этот ответ был действительно для моего другого вопроса: stackoverflow.com/questions/2291364, может быть, переместить его туда? - person Rory; 23.02.2010
comment
Насколько я помню, я проверил модульные тесты sqlite, используя старый исходный элемент управления для дозвукового (теперь его нет). Я не проверял их на github, только файл .cs провайдера. Я могу проверить модульные тесты на github в ближайшем будущем. Я обнаружил, что проще скопировать модульные тесты в новый проект внутри дозвукового источника, а затем изменить его по мере необходимости для sqlite, отредактировать app.config и т. Д. Затем необходимо добавить новый файл базы данных northwind.sqlite. Мне нужно заново научиться работать с github, а затем надеюсь, что Роб разрешит регистрацию для всего этого. - person P a u l; 24.02.2010