2016-06-28 7 views
0

sagen, dass ich die folgenden SQL-Anweisungen, die ich bin Ausführung ExecuteNonQuery(DbCommand) von C# in einer Web-Anwendung mitExecute Eine SQL-Block ohne Unterbrechung

DECLARE @InsertedProductID INT -- this is passed as a parameter 
DECLARE @GroupID INT -- this is passed as a parameter 
DECLARE @total INT 
SET @total = (SELECT COUNT (*) FROM Products WHERE GroupID = @GroupID) 
UPDATE Products SET ProdName = 'Prod_'+ CAST(@total as varchar(15)) 
WHERE ProductID = @InsertedProductID 

Mein Problem ist, dass ich sicherstellen wollen, dass der gesamte Block ausführt ein. Mein Ziel ist es, immer die ProdName einzigartig pro Gruppe zu haben. Wenn ich alles so belasse, wie es ist, besteht eine gute Chance, dass ich doppelte Produktnamen bekomme, wenn ein insert zwischen dem Erhalt des @total und der Ausführung des UPDATE stattfindet. Gibt es eine Möglichkeit, sicherzustellen, dass der gesamte SQL-Block sofort und ohne Unterbrechung ausgeführt wird? Will exec oder sp_executesql dies erreichen? Mein letzter Ausweg wäre ein lock um die ExecuteNonQuery(DbCommand) zu setzen. Aber das mag ich nicht, da es einen Flaschenhals schaffen würde. Ich denke nicht, dass die Verwendung einer SQL-Transaktion hier hilfreich ist, da ich mir keine Gedanken über die Integrität der Befehle mache, sondern eher über die Parallelität der Befehle besorgt bin.

Antwort

3

Im Allgemeinen platziert jede DML-Anweisung (UPDATE/INSERT/DELETE) eine Sperre (Zeilen-/Tabellenebene) in der jeweiligen Tabelle, aber wenn Sie ausdrücklich garantieren möchten, dass Ihre Operation keine andere ausführende Anweisung stören sollte, sollten Sie diese gesamte Tabelle berücksichtigen SQL-Block in einem Transaktionsblock sagen

Begin transaction 
begin try 
DECLARE @InsertedProductID INT -- this is passed as a parameter 
DECLARE @GroupID INT -- this is passed as a parameter 
DECLARE @total INT 
SET @total = (SELECT COUNT (*) FROM Products WHERE GroupID = @GroupID) 
UPDATE Products SET ProdName = 'Prod_'+ CAST(@total as varchar(15)) WHERE ProductID = @InsertedProductID 

commit; // commits the transaction 
end try 
begin catch 
rollback; //Rolls back the transaction 
end catch 
end 

Sie sollten auch Transaction Isolation Level zu READ COMMITTED machen die betrachten dirty reads zu vermeiden. Auch offensichtlich sollten Sie diese gesamte Logik in einem stored procedure wickeln sie eher als adhoc SQL

3

Ausführung Wenn Sie die Kontrolle über die Erstellung Ihrer SqlConnection-Objekte haben, sollten Sie auf Datenbanksperren unter Berufung mit Transactions und eine entsprechende IsolationLevel. Wenn Sie beispielsweise Snapshot verwenden, führt dies dazu, dass die zweite Transaktion fehlschlägt, wenn eine separate Transaktion die Daten berührt hat, bevor die Festschreibung stattgefunden hat.

Etwas wie:

var c = new SqlConnection(...); 
var tran1 = c.BeginTransaction(IsolationLevel.Snapshot); 
var tran2 = c.BeginTransaction(IsolationLevel.Snapshot); 
DoStuff(c, tran1);//Touch some database data 
tran1.Commit(); 
DoStuff(c, tran2);//Change the same data 
tran2.Commit();//Error! 
1

nicht so sicher konnte man nicht einfach tun, um diese

UPDATE Products 
SET ProdName = 'Prod_'+ CAST((SELECT COUNT (*) 
           FROM Products 
           WHERE GroupID = @GroupID) as varchar(15)) 
WHERE ProductID = @InsertedProductID 

Aber für mich ist das eine ungerade Update

1

eine Transaktion zu verwenden ist der richtige Weg gehen. Zusammen mit den anderen Antworten können Sie auch TransactionScope verwenden. Die TransactionScope impliziert implizit den Verbindungs- und den SQL-Befehl in einer Transaktion. Ein Rollback wird automatisch ausgeführt, wenn ein Problem auftritt, da sich das TransactionScope in einem Verwendungsblock befindet.

Beispiel:

 try 
     { 
      using (var scope = new TransactionScope()) 
      { 
       using (var conn = new SqlConnection("your connection string")) 
       { 
        conn.Open(); 
        var cmd = new SqlCommand("your SQL here", conn); 
        cmd.ExecuteNonQuery(); 
       } 

       scope.Complete(); 
      } 
     } 
     catch (TransactionAbortedException ex) 
     { 

     } 
     catch (ApplicationException ex) 
     { 

     }