Pregunta

Me estoy poniendo excepciones de bloqueo cuando se trata de utilizar transacciones con SubSonic y SQLite. Estoy usando esto desde un solo hilo y no hay otros procesos que acceden a mi db, así que realmente no esperaba ningún tipo de problemas.

Si escribo un código como éste a continuación, me sale una excepción en la segunda llamada a Save () dentro del bucle - por lo que la tercera llamada a Save () sobre toda

.
       using (TransactionScope ts = new TransactionScope())
       {
            using (SharedDbConnectionScope sharedConnectinScope = new SharedDbConnectionScope())
            { 
                SomeDALObject x = new SomeDALObject()
                x.Property1 = "blah";
                x.Property2 = "blah blah";
                x.Save();

                foreach (KeyValuePair<string, string> attribute in attributes)
                { 
                    AnotherDALObject y = new AnotherDALObject()
                    y.Property1 = attribute.Key
                    y.Property2 = attribute.Value

                    y.Save();  // this is where the exception is raised, on the 2nd time through this loop
                }
            }
       }

Si tengo las declaraciones utilizando (como arriba), o si acabo de using (TransactionScope ts = new TransactionScope()) cuando me siento un System.Data.SQLite.SQLiteException con el mensaje

  

El archivo de base de datos está bloqueado

     

base de datos está bloqueado

El seguimiento de pila es:

   at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
   at System.Data.SQLite.SQLiteDataReader.NextResult()
   at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
   at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
   at System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock)
   at System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
   at System.Data.SQLite.SQLiteConnection.BeginTransaction()
   at System.Data.SQLite.SQLiteEnlistment..ctor(SQLiteConnection cnn, Transaction scope)
   at System.Data.SQLite.SQLiteConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SQLite.SQLiteConnection.Open()
   at SubSonic.SQLiteDataProvider.CreateConnection(String newConnectionString)
   at SubSonic.SQLiteDataProvider.CreateConnection()
   at SubSonic.SQLiteDataProvider.ExecuteScalar(QueryCommand qry)
   at SubSonic.DataService.ExecuteScalar(QueryCommand cmd)
   at SubSonic.ActiveRecord`1.Save(String userName)
   at SubSonic.ActiveRecord`1.Save()
   at (my line of code above).

Si tengo las declaraciones provistas utilizando anidados al revés, con SharedDbConnectionScope en el exterior, cuando me siento un TransactionException con el mensaje "La operación no es válida para el estado de la transacción." seguimiento de la pila es:

at System.Transactions.TransactionState.EnlistVolatile(InternalTransaction tx, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistVolatile(IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions)
   at System.Data.SQLite.SQLiteEnlistment..ctor(SQLiteConnection cnn, Transaction scope)
   at System.Data.SQLite.SQLiteConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SQLite.SQLiteConnection.Open()
   at SubSonic.SQLiteDataProvider.CreateConnection(String newConnectionString)
   at SubSonic.SQLiteDataProvider.CreateConnection()
   at SubSonic.SQLiteDataProvider.ExecuteScalar(QueryCommand qry)
   at SubSonic.DataService.ExecuteScalar(QueryCommand cmd)
   at SubSonic.ActiveRecord`1.Save(String userName)
   at SubSonic.ActiveRecord`1.Save()
   at (my line of code above)

y la excepción interna es "Tiempo de espera de transacciones"

No tengo ningún código personalizado en mis clases DAL generadas, o cualquier otra cosa inteligente que se me ocurre que estaría causando esto.

A alguien más tropezado con problemas de transacción de este tipo, o alguien puede sugerir dónde empezar a buscar el problema?

Gracias!

UPDATE: Me he dado cuenta mención de cosas relacionados con la transacción en las notas de la versión para las versiones 1.0.61-65 (por ejemplo, aquí ), por lo que quizá la actualización SubSonic para trabajar con la versión más reciente del proveedor de datos .NET solucionaría algunos de estos problemas ...

¿Fue útil?

Solución

Al hacer el proveedor de sqlite revisado para 2.x subsónica he creado un conjunto completo de pruebas de unidad en base a las pruebas existentes sqlserver subsónicas. (Estas pruebas también se puso en contacto con el código revisado.) Las únicas pruebas que fallaron fueron los relacionados con las transacciones (tal vez los de migración también). "El archivo de base de datos está bloqueado" mensaje de error, como has visto. Subsónica fue escrito principalmente para el servidor SQL que no hace bloqueo a nivel de archivos como SQLite, por lo que algunas cosas no funcionan; tendría que ser reescrito para manejar esto mejor.

Nunca he utilizado el TransactionScope ya que tienes. Hago mis subsónicas 2.2 transacciones de este tipo, y hasta ahora no hay problemas con el proveedor de SQLite. Puedo confirmar que es necesario utilizar transacciones con SQLite si se trata de varias filas o que es muy lento.

public void DeleteStuff(List<Stuff> piaRemoves)
{
    QueryCommandCollection qcc = new QueryCommandCollection();

    foreach(Stuff item in piaRemoves)
    {
        Query qry1 = new Query(Stuff.Schema);
        qry1.QueryType = QueryType.Delete;
        qry1.AddWhere(Stuff.Columns.ItemID, item.ItemID);
        qry1.AddWhere(Stuff.Columns.ColumnID, item.ColumnID);
        qry1.AddWhere(Stuff.Columns.ParentID, item.ParentID);
        QueryCommand cmd = qry1.BuildDeleteCommand();
        qcc.Add(cmd);
    }
    DataService.ExecuteTransaction(qcc);
}

Otros consejos

Terminé usando la sugerencia de Paul y volvió a escribir el código para algo como esto:

    QueryCommandCollection qcc = new QueryCommandCollection();

    SomeDALObject x = new SomeDALObject()
    x.Property1 = "blah";
    x.Property2 = "blah blah";
    qcc.Add(x.GetSaveCommand());

    foreach (KeyValuePair<string, string> attribute in attributes)
    { 
        AnotherDALObject y = new AnotherDALObject()
        y.Property1 = attribute.Key
        y.Property2 = attribute.Value

        qcc.Add(y.GetSaveCommand());
    }

    DataService.ExecuteTransaction(qcc);

Este es de hecho mucho mejor, ya que toda la preparación para los accesos de bases de datos se realiza antes de que se abra la transacción, por lo tanto, la transacción será abierta por mucho menos tiempo.

Esto no funcionará tan bien si usted necesita para volver identificadores generados automáticamente con el fin de ejecutar inserciones para los registros secundarios; tendrá que utilizar un enfoque diferente para eso.

I después haga clic en algunos otros problemas de enhebrado / transacción: cuando tuve múltiples hilos de ejecución DataService.ExecuteTransaction () al mismo tiempo, me gustaría tener AccessViolationExceptions y NullReferenceExceptions, básicamente, un poco de un desastre. Pero cambiar de usar tenedor de Pablo de SubSonic con el SQLDataProvider actualizada y también cambiando a utilizar < a href = "http://sourceforge.net/projects/sqlite-dotnet2/" rel = "nofollow noreferrer"> System.Data.SQLite v1.0.65.0 parece fijo instantáneamente. Hooray!

ACTUALIZACIÓN: En realidad Todavía estoy encontrando problemas de roscado utilizando SubSonic con SQLite. Básicamente, el SQLiteDataProvider en SubSonic no está escrito para hacer frente a múltiples hilos. Más por venir ...

Estamos utilizando SQL Lite para probar son el código relacionado con las acciones de base de datos. Lite SQL no admite transacciones anidadas. Hemos tenido problemas similares en los que tuvimos transacción NHibernate y .Net. En última instancia, tuvimos que sentar la cabeza utilizando SQL Express para probar el código relacionado con la base de datos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top