problèmes de verrouillage avec SQLite et subsonique lors de l'utilisation des transactions sur un seul thread

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

Question

Je suis en train de verrouillage des exceptions lorsque vous essayez d'utiliser des transactions avec subsonique et SQLite. J'utilise ce à partir d'un seul fil et il n'y a pas d'autres processus d'accès à mon db, donc je ne m'y attendais pas vraiment de tels problèmes.

Si j'écrire du code comme ceci ci-dessous, je reçois une exception sur le deuxième appel à Save () dans la boucle - de sorte que le troisième appel à Save () sur tout

.
       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 je l'aide de () les déclarations ci-dessus, ou si je viens using (TransactionScope ts = new TransactionScope()) puis-je obtenir un System.Data.SQLite.SQLiteException avec le message

  

Le fichier de base de données est verrouillé

     

base de données est verrouillée

La trace de pile est:

   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 je l'aide de statments imbriquées l'inverse, avec SharedDbConnectionScope à l'extérieur, puis-je obtenir un TransactionException avec le message « L'opération est valable pour l'état de la transaction. » Trace de la pile est:

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)

et l'exception interne est "Transaction Timeout"

Je n'ai pas de code personnalisé dans mes classes DAL générés, ou toute autre chose intelligente qui je peux penser à ce serait la cause de ce.

Quelqu'un d'autre a rencontré des problèmes comme celui-ci ou quelqu'un peut-il suggérer transaction où je commence à chercher le problème?

merci!

Mise à jour: Je remarque mention des choses liées à la transaction dans les notes de version pour les versions 1.0.61-65 (par exemple ici ), donc peut-être mettre à jour subsonique à travailler avec la dernière version du .NET Data Provider résoudrait certaines de ces questions ...

Était-ce utile?

La solution

En fait le fournisseur de sqlite révisé pour subsonique 2.x J'ai créé un ensemble complet de tests unitaires sur la base des essais subsoniques SQLServer existants. (Ces tests ont également été vérifiés avec le code révisé.) Les seuls tests qui ont échoué étaient ceux liés aux transactions (peut-être les Ones de migration trop). « Le fichier de base de données est verrouillé » message d'erreur, comme vous l'avez vu. Subsonic a été principalement écrit pour le serveur SQL qui ne fait pas un verrouillage de fichier comme SQLite fait, de sorte que certaines choses ne fonctionnent pas; il devrait être réécrite pour gérer ce mieux.

Je ne l'ai jamais utilisé TransactionScope que vous avez. Je fais mes subsoniques 2.2 transactions comme celui-ci, et à ce jour aucun problème avec le fournisseur de SQLite. Je peux confirmer que vous avez besoin d'utiliser des transactions avec SQLite traitant de plusieurs lignes ou il est vraiment lent.

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

Autres conseils

Je fini par utiliser la suggestion de Paul et réécris mon code à quelque chose comme ceci:

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

Ceci est en fait beaucoup mieux depuis que toute la préparation pour les coups de base de données sont effectuées avant que la transaction est ouverte, par conséquent, la transaction sera ouverte pour beaucoup moins de temps.

Cela ne fonctionnera pas si bien si vous avez besoin pour obtenir les ID générés automatiquement afin d'exécuter des dossiers INSERTs enfants; vous aurez besoin d'utiliser une approche différente pour cela.

Je puis appuyez sur d'autres threading / problèmes de transaction: quand j'avais plusieurs threads d'exécution DataService.ExecuteTransaction () en même temps je recevrais AccessViolationExceptions et NullReferenceExceptions, en fait un peu de désordre. Mais le changement à utiliser fourchette de Paul de SubSonic la SQLDataProvider mise à jour et de modifier également utiliser < a href = "http://sourceforge.net/projects/sqlite-dotnet2/" rel = "nofollow noreferrer"> System.Data.SQLite v1.0.65.0 semble instantanément fixe elle. Hourra!

Mise à jour: En fait, je suis toujours des problèmes de filetage rencontre à l'aide subsonique avec SQLite. Fondamentalement, le SQLiteDataProvider en subsonique est pas écrit pour traiter multithreading. Plus à venir ...

Nous utilisons lite SQL pour tester le code sont liés à des actions de base de données. SQL Lite ne supporte pas les transactions imbriquées. Nous avons eu des problèmes similaires où nous avons eu transaction NHibernate et .Net. En fin de compte, nous avons dû installer en utilisant SQL Express pour tester la base de données le code correspondant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top