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

StackOverflow https://stackoverflow.com/questions/2283063

Вопрос

Я использую SubSonic 2.2 и sqlite и столкнулся с проблемой при работе с таблицами со столбцом ПЕРВИЧНОГО КЛЮЧА INTEGER, который не является АВТОИНКРЕМЕНТНЫМ.В соответствии с Вопросы и ответы:

Если вы объявляете столбец таблицы ЦЕЛОЧИСЛЕННЫМ ПЕРВИЧНЫМ КЛЮЧОМ, то всякий раз, когда вы вставляете NULL в этот столбец таблицы, NULL автоматически преобразуется в целое число, которое на единицу больше наибольшего значения этого столбца по сравнению со всеми другими строками таблицы, или 1, если таблица пуста.

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

В моем приложении значения моих идентификаторов генерируются из удаленной базы данных, поэтому я не хочу автоматически генерировать их в sqlite.Это не должно быть проблемой:Я просто буду указывать значения при создании записей в этой таблице.Однако, когда я использую SubSonic sonic.exe для автоматической генерации моего DAL, столбцу первичного ключа присваивается значение AutoIncrement = true.Похоже, это означает, что я не могу установить столбец ID - ActiveHelper subsonic .GetInsertCommand() игнорирует его, поскольку считает, что он сгенерирован автоматически.

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

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

Я предполагаю, что решение состоит либо в том, чтобы

  • Не использовать ЦЕЛОЧИСЛЕННЫЕ столбцы ПЕРВИЧНОГО КЛЮЧА для ключей, которые генерируются в другом месте, или

  • Измените шаблоны, чтобы для такого рода столбцов не было установлено значение AutoIncrement = true.Это означало бы, что SubSonic никогда не будет рассматривать их как автоматическое увеличение, поэтому мне нужно быть осторожным, чтобы позже не ожидать получения автоматически сгенерированных значений.К сожалению, я не думаю, что в шаблонах возможно легко определить, действительно ли столбец является АВТОИНКРЕМЕНТНЫМ или нет, поэтому, возможно, мне пришлось бы вместо этого выполнить какое-нибудь уродливое жесткое кодирование....

Есть еще какие-нибудь мысли или предложения?

Это было полезно?

Решение

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

В большинстве случаев это неоптимально, поскольку возвращает ограниченную информацию.В данном случае - это не говорит нам, является ли столбец АВТОИНКРЕМЕНТНЫМ или нет.Вероятно, есть лучший способ запросить у SQLite метаинформацию из таблицы - но, к сожалению, он не использовался.

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

Другие советы

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

// Обнаружение автоинкремента теперь доступно в последнем System.Data.SQLite.1.0.60.0 -- пол столбец.АвтоИнкремент = Преобразовать.ToBoolean(строка["АВТОИНКРЕМЕНТ"]);

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

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

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

Преобразовать базу данных SQL Server в базу данных SQLite http://www.codeproject.com/KB/database/convsqlservertosqlite.aspx

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

В System.Data.SQLite doc говорится: "Вы можете создать несколько потоков, и эти потоки могут создавать свои собственные 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);
    }

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top