Inserción de valores de clave primaria utilizando SubSonic y SQLite - SubSonic problema porque piensa columnas son o bien AutoIncrement o no

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

Pregunta

Estoy usando SubSonic SQLite 2.2 y y he encontrado un problema cuando se trata de tablas con una columna INTEGER PRIMARY KEY que no es incrementado automáticamente. De acuerdo con la FAQ :

  

Si se declara una columna de una tabla para ser INTEGER PRIMARY KEY, a continuación, cada vez que se inserta un NULL en que la columna de la tabla, el NULL se convierte automáticamente en un número entero que es uno mayor que el valor más grande de que en columna sobre todas las otras filas de la tabla, o 1 si la tabla está vacía.

Así sqlite piensa que estas columnas son a veces Auto incrementado (es decir, justo cuando se proporcionan los valores NULL). El problema es que SubSonic piensa que son siempre Auto incrementado.

En mi solicitud mis valores de ID se generan a partir de una base de datos remota, por lo que no quieren generar automáticamente en SQLite. Esto no debería ser un problema: me limito a indicar los valores cuando se crea registros en esta tabla. Sin embargo, cuando uso sonic.exe de SubSonic a generar automáticamente mi DAL, la columna de clave principal se establece en AutoIncrement = true. Esto parece significar que no puedo fijar la columna ID - ActiveHelper.GetInsertCommand de subsónica () ignora, ya que piensa que es auto-generada

.

La línea donde determina si se trata de autoincrement o no está en SubSonic.SQLiteDataProvider.GetTableSchema ():

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

Creo que la solución es o bien

  • No utilizar INTEGER columnas PRIMARY KEY para las claves que se generan en otro lugar, o

  • Modificar las plantillas por lo que estos tipos de columnas no se establecen en AutoIncrement = true. Esto significaría SubSonic no siempre va a tratarlos como incremento automático, por lo que había que tener cuidado de que no me tarde esperan conseguir los valores generados automáticamente. Por desgracia, no creo que sea posible dentro de las plantillas para determinar fácilmente si la columna es realmente AutoIncrement o no, así que tal vez tendría que hacer algo feo-codificación duro en lugar ....

¿Alguna otra idea o sugerencia?

¿Fue útil?

Solución

Por desgracia, parece que nuestro SQLiteDataProvider supone que si se trata de una PK Int64, entonces es el incremento automático. Estoy mirando por encima de la fuente en este momento (que no he escrito ese proveedor) y puedo ver que la forma en que se está cargando el esquema está utilizando Connection.GetSchema - que utiliza construido System.Data.Common.DbConnection para obtener el esquema para las tablas.

Este es subóptima mayor parte del tiempo, ya que devuelve información limitada. En este caso - no nos dice si la columna es incrementado automáticamente o no. Hay probablemente una mejor manera de pedir SQLite la metainformación sobre la mesa -. Pero no se utilizó por desgracia

Respuesta corta:. Definir una nueva PK, si es posible, y utilizar la otra clave como referencia

Otros consejos

Como ya he mencionado antes de registrarme en un SQLiteDataProvider revisada en diciembre. Compruebe que en la línea 407 en SQLiteDataProvider.cs tiene:

Detección // Autoincrement ahora disponible en System.Data.SQLite reciente. 1.0.60.0 - paul column.AutoIncrement = Convert.ToBoolean (fila [ "AutoIncrement"]);

Hay también varias otras mejoras y correcciones de errores en las líneas circundantes. El nuevo código no ha sido añadido a la distribución principal proyecto en GitHub supongo, no sigo el proyecto demasiado. SQLite ha sido un proveedor maravilloso aparte del bloqueo a nivel de archivo. Tengo una versión de cosecha propia de System.Data.SQLite que utiliza las nuevas características clave externa de SQLite, y la versión oficial se debe hacer este mes?

Esta es la versión revisada: SQLiteDataProvider.cs

Por cierto, echa un vistazo a este proyecto en caso de que necesite convertir de SQL Server:

Convertir a SQL Server DB SQLite DB http://www.codeproject.com/KB/database/convsqlservertosqlite.aspx

Me parece que no puedo utilizar un CreateConnection escrita como en SQLDataProvider debido al bloqueo de archivos. El CreateConnection en SQLiteDataProvider como lo es ahora que está mal ya que ignora las nuevas cadenas de conexión.

El doc System.Data.SQLite dice "Usted puede crear múltiples hilos, y esos hilos puede crear sus propios SQLiteConnection y siguientes objetos para acceder a una base de datos. Múltiples conexiones en múltiples hilos en el mismo archivo de base de datos son perfectamente aceptables y se comportarán predecible ".

Así que lo que he probado es la siguiente, y es realmente cludgey. Utilizar un diccionario de conexiones tecleados por el ID del tema y la cadena de conexión. Pero todas las pruebas unitarias Pass, incluyendo la mayoría de las transacciones (necesita mejores pruebas). Escribí un par de pruebas de transacción, la sección cerraduras críticos, y yo creo que puede ser hilo de seguridad, sólo hay pruebas más 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top