2009-12-06 3 views
1

J'écris un programme en C# qui doit insérer des données dans plusieurs tables. La première insertion renvoie un dernier insert rowid, qui est à son tour utilisé pour effectuer une insertion sur les autres tables. Dans le code pseudo:Insertion fiable avec SQLite

INSERT INTO TableFoo (Col2, Col3, Col4, etc.) VALUES (@Bla2, @bla3, @bla4); 

Immédiatement après cet insert je reçois le dernier id d'insertion par:

SELECT last_insert_rowid() AS RowId; 

je récupère la colonne RowId dans un datareader et le stocker dans un int "RowId". Enfin, j'insérer des tables supplémentaires comme:

INSERT INTO TableBar (FooId, Col2) VALUES (RowId, @BSomeMoreBla)"); 

La question: Si possible, je veux réaliser ce groupe d'inserts (et sélectionnez RowId) dans 1 transaction pour empêcher certaines données demi-enregistrée. Est-il possible d'utiliser une transaction dans ce scénario et quelle serait la méthode préférée?

Une transaction SQLite? Espace de noms System.Transactions de C#? Une troisième façon d'effectuer une base de données atomique multi-insert ...?

Répondre

4

ryber m'a aidé tout à fait sur mon chemin, mais j'ai trouvé la solution que je recherchais par moi-même. Pour ceux qui pourraient être intéressés, voici mon code (peut-être un peu finetuning peut-être, mais parfaitement le code légitime et utilisable):

nb. tbl1 a un PK auto incrémentant, tbl2 et tbl3 dépendent du PK de tbl1 via un FK. Si une exception se produit, aucune des requêtes n'est validée, tout comme une transaction correcte devrait se comporter ;-). Seulement si Commit() est exécuté sans erreurs, tout le groupe est inséré, même s'il y a un SELECT au milieu de la route.

try { 
    transaction = connection.BeginTransaction(); 

    command = new SQLiteCommand(); 
    command.Transaction = transaction; 
    command.CommandText = "INSERT INTO tbl1 (data) VALUES ('blargh')"; 
    command.ExecuteNonQuery(); 

    command = new SQLiteCommand(); 
    command.Transaction = transaction; 
    command.CommandText = "SELECT last_insert_rowid()"; 
    rowId = Convert.ToInt64(command.ExecuteScalar()); 

    command = new SQLiteCommand(); 
    command.Transaction = transaction; 
    command.CommandText = "INSERT INTO tbl2 (id, tbl1Id) VALUES (2, @rowId)"; 
    command.Parameters.Add(new SQLiteParameter("@rowId", rowId)); 
    command.ExecuteNonQuery(); 

    command = new SQLiteCommand(); 
    command.Transaction = transaction; 
    command.CommandText = "INSERT INTO tbl3 (id, tbl1Id) VALUES (3, @rowId)"; 
    command.Parameters.Add(new SQLiteParameter("@rowId", rowId)); 
    command.ExecuteNonQuery(); 

    transaction.Commit(); 
} 
catch (Exception ex) { 
    if(connection.State == ConnectionState.Open) 
     transaction.Rollback(); 
    MessageBox.Show(ex.Message); 
} 
finally { 
    connection.Close(); 
    transaction.Dispose(); 
    command.Dispose(); 
} 
2

Il doit absolument s'agir d'une transaction SQLite. Votre meilleur pari est d'utiliser un fournisseur de données Sqlite comme celui-ci: http://sqlite.phxsoftware.com/ Je ne suis pas sûr que l'ADO.Net de base fonctionnera correctement.

EDIT POUR UN COMMENTAIRE:

Ok, vous voulez juste faire une transaction comme vous le feriez de toute autre opération ADO:

 SqliteCommand command = connection.CreateCommand(); 
     SqlTransaction transaction = null; 

     connection.Open(); 

     transaction = connection.BeginTransaction(); 

     command.Transaction = transaction; 

     command.CommandText = "Insert bla"; 
     command.ExecuteNonQuery(); 

     command.CommandText = "Update bla"; 
     command.ExecuteNonQuery(); 

     transaction.Commit(); 
     connection.Close(); 

En général, vous voulez également mettre tout cela dans un try/catch avec une annulation

Remarque: Im pas 100% sur cette commande SQlLite. Jetez un oeil à votre documentation ... mais le reste devrait être bon

+0

J'ai le System.Data.SQLite en cours d'utilisation depuis un certain temps déjà, et cela fonctionne parfaitement, mais je suis un total négligeable concernant les transactions. Voilà pourquoi je demande quelques conseils :). – Webleeuw

+0

Ah ok, ça le rend beaucoup plus clair maintenant, merci. Mais le noyau de la question était en fait si un SELECT dans une transaction est possible et si je peux utiliser le select pour récupérer une valeur utilisée par les autres inserts, le tout dans la même transaction? – Webleeuw

+0

Oui, tant que le select est enrôlé dans la transaction comme ci-dessus, vous devriez être bien – ryber