A inserção de valores de chave primária usando Subsônicas e sqlite - problema porque Subsónico pensa colunas são AutoIncrement ou não

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

Pergunta

Eu estou usando Subsónico 2.2 e sqlite e ter encontrado um problema ao lidar com tabelas com um número INTEIRO de coluna de CHAVE PRIMÁRIA que não é AUTOINCREMENT.De acordo com o perguntas frequentes:

Se você declarar uma coluna de uma tabela a ser INTEGER CHAVE PRIMÁRIA e, em seguida, sempre que você inserir um valor NULO na coluna da tabela, o NULO é automaticamente convertido em um número inteiro que é maior do que o maior valor da coluna sobre todas as outras linhas da tabela, ou 1 se a tabela está vazia.

Assim sqlite acha que essas colunas são às vezes auto incrementado (ou seja, apenas quando os valores NULOS são fornecidas).O problema é que Subsónico pensa que eles são sempre auto incrementado.

No meu aplicativo meu ID de valores de são gerados a partir de um banco de dados remoto, então eu não quero para auto-gerar-los em sqlite.Isso não deve ser problema:Eu simplesmente irá fornecer valores quando eu criar registros nesta tabela.No entanto, quando eu usar o SubSonic é sonic.exe para auto-gerar meu DAL, a coluna de chave primária é definida como AutoIncrement = true.Isso parece significar que eu não posso definir o ID de coluna - subsónico do ActiveHelper.Método getinsertcommand() ignora-lo, pois acha que é auto-gerado.

A linha onde ele determina se ela é de incremento automático ou não está em Subsónico.SQLiteDataProvider.GetTableSchema():

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

Eu acho que a solução é

  • Não usar INTEIRO colunas de CHAVE PRIMÁRIA para as chaves são geradas em outro lugar, ou

  • Modificar os modelos para esses tipos de colunas não estão definidas para AutoIncrement = true.Isso significaria Subsónico não tratá-los como auto incremento, então eu preciso ter o cuidado para que eu não mais tarde de esperar para obter auto-gerados valores.Infelizmente eu não acho que é possível nos modelos facilmente determinar se a coluna é realmente AUTOINCREMENT ou não, talvez por isso eu tenho que fazer algumas feio rígido codificação em vez disso....

Quaisquer outros pensamentos ou sugestões?

Foi útil?

Solução

Infelizmente, parece que o nosso sqlitedataprovider assume que, se for um PK INT64, é um incremento automático. Estou olhando a fonte agora (não escrevi este provedor) e posso ver que a maneira como o esquema está sendo carregado está usando o Connection.getSchema - que usa o Built System.data.common.dbconnection para obter o esquema para as tabelas.

Isso é abaixo do ideal na maioria das vezes porque retorna informações limitadas. Nesse caso - não está nos dizendo se a coluna é autoincrement ou não. Provavelmente, há uma maneira melhor de perguntar à SQLite as meta informações sobre a mesa - mas infelizmente não foram usadas.

Resposta curta: Defina um novo PK, se puder, e use a outra chave como referência.

Outras dicas

Como mencionei antes de verifiquei um SQLitedATAPROVider revisado em dezembro. Verifique isso na linha 407 em sqlitedataprovider.cs que você tem:

// Detecção de increment automaticamente agora disponível no System.data.sqlite recente. 1.0.60.0 - Paul Column.autoinCrement = Convert.tobeoLean (linha ["AutoinCrement"]);

Existem também várias outras melhorias e correções de bugs nas linhas circundantes. O novo código nunca foi adicionado à distribuição principal do projeto no Github, eu acho, não sigo muito o projeto. O SQLite tem sido um fornecedor maravilhoso, além do bloqueio no nível do arquivo. Eu tenho uma versão em casa do System.Data.Sqlite que usa os novos recursos de chave estrangeira do SQLite, e a versão oficial deve ser feita este mês?

Aqui está a versão revisada:Sqlitedataprovider.cs

BTW, confira este projeto, caso você precise converter do SQL Server:

Converta o db de servidor SQL para sqlite dbhttp://www.codeproject.com/kb/database/convsqlserverposqlite.aspx

Eu acho que eu não posso usar um CreateConnection escrito como no SqlDataProvider por causa do bloqueio de arquivo.O CreateConnection em SQLiteDataProvider como ele é agora, é errado, pois ignora novas seqüências de caracteres de conexão.

O Sistema.Dados.SQLite doc diz "Você Pode criar vários threads, e os threads podem criar seus próprios SQLiteConnection e subsequente objetos para acesso a um banco de dados.Várias conexões em vários segmentos para o mesmo arquivo de banco de dados são perfeitamente aceitáveis e irá comportar-se de maneira previsível."

Então, o que eu tentei é o seguinte, e é realmente cludgey.Usar um dicionário de conexões de encaixe por rosca e id de seqüência de caracteres de conexão.Mas todos os testes de unidade de passagem, incluindo a maioria das transações (necessidades de testes).Eu escrevi mais um par de testes de transação, com seção crítica bloqueios, e eu acho que pode ser thread-safe, só precisa de mais testes realistas.

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

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top