Auf Englisch lesen

Freigeben über


IsolationLevel Enumeration

Definition

Gibt das Sperrverhalten für Transaktionen für die Verbindung an.

Diese Enumeration unterstützt eine bitweise Kombination ihrer Memberwerte.

C#
public enum IsolationLevel
C#
[System.Flags]
public enum IsolationLevel
Vererbung
IsolationLevel
Attribute

Felder

Name Wert Beschreibung
Chaos 16

Die ausstehenden Änderungen von höher isolierten Transaktionen können nicht überschrieben werden.

ReadCommitted 4096

Beim Lesen der Daten werden gemeinsame Sperren verwendet, um das Lesen geänderter Daten zu verhindern. Die Daten können jedoch vor dem Ende der Transaktion geändert werden, was zu nicht wiederholbaren Lesevorgängen oder Phantomdaten führen kann.

ReadUncommitted 256

Ein Lesen geänderter Daten ist möglich. Das heißt, dass keine gemeinsamen Sperren ausgegeben und keine exklusiven Sperren berücksichtigt werden.

RepeatableRead 65536

Die Sperren gelten für alle in einer Abfrage verwendeten Daten, damit die Daten nicht durch andere Benutzer aktualisiert werden können. Nicht wiederholbare Lesevorgänge werden dadurch verhindert, es sind jedoch weiterhin Phantomzeilen möglich.

Serializable 1048576

Eine Bereichssperre wird für die DataSet-Klasse festgelegt. Dadurch wird verhindert, dass andere Benutzer vor dem Abschluss der Transaktion Zeilen in das Dataset einfügen oder darin aktualisieren.

Snapshot 16777216

Reduziert das Blockieren durch Speichern einer Version von Daten, die von einer Anwendung gelesen werden können, während sie von einer anderen Anwendung geändert werden. Gibt an, dass Sie von einer Transaktion aus keine Änderungen sehen können, die in anderen Transaktionen vorgenommen wurden, auch wenn Sie diese erneut abfragen.

Unspecified -1

Es wird nicht der angegebene Isolationsgrad verwendet, der Grad kann jedoch nicht bestimmt werden.

Beispiele

Diese Anwendung veranschaulicht, wie in DbTransactionverwendet IsolationLevel wird. Das Beispiel zeigt, welches der folgenden Verhaltensweisen in den verschiedenen Isolationsstufen zulässig ist:

  • Schmutzige Lesevorgänge.

  • Nicht wiederholbare Lesevorgänge.

  • Phantome.

Diese Anwendung wird in den folgenden Isolationsstufen ausgeführt:

  • ReadUncommitted

  • ReadCommitted

  • RepeatableRead

  • Serialisierbar

  • Momentaufnahme

Die PhantomReadThreads-Klasse veranschaulicht, ob die bestimmte Transaktion das Phantom Read-Verhalten zulässt. Wenn die Transaktion das Verhalten zulässt, werden die Threads in der folgenden Reihenfolge ausgeführt:

  • Wählen Sie im ersten Thread die Produkte (Alle) aus.

  • Fügen Sie im zweiten Thread ein neues Produkt ein.

  • Commit für die Transaktion im zweiten Thread.

  • Wählen Sie die Produkte erneut aus.

  • Commit für die Transaktion im ersten Thread.

Wenn die Transaktion das Verhalten zulässt, erhalten die beiden Select-Vorgänge die unterschiedlichen Ergebnisse.

Die NonrepeatableReadThreads-Klasse veranschaulicht, ob die bestimmte Transaktion das Verhalten nicht rerepeatable Read zulässt. Wenn die Transaktion das Verhalten zulässt, werden die Threads in der folgenden Reihenfolge ausgeführt:

  • Wählen Sie im ersten Thread das Produkt (ProductId=1) aus.

  • Aktualisieren Sie im zweiten Thread den Quantity-Wert(ProductId=1).

  • Commit für die Transaktion im zweiten Thread.

  • Wählen Sie das Produkt erneut aus.

  • Commit für die Transaktion im ersten Thread.

Wenn die Transaktion das Verhalten zulässt, erhalten die beiden Select-Vorgänge die unterschiedlichen Ergebnisse.

Die ExchangeValuesThreads-Klasse veranschaulicht den Unterschied zwischen der Serializable- und Snapshot-Transaktion. Für die serialisierbare Transaktion werden Threads in der folgenden Reihenfolge ausgeführt:

  • Rufen Sie im ersten Thread den Preis des Produkts(ProductId=2) ab, und speichern Sie sie in der Variablen.

  • Aktualisieren Sie im ersten Thread den Preis des Produkts(ProductId=1) mit dem Preis des Produkts(ProductId=2).

  • Commit für die Transaktion im ersten Thread.

  • Rufen Sie im zweiten Thread den Preis des Produkts (ProductId=1) ab, und speichern Sie sie in der Variablen.

  • Aktualisieren Sie im zweiten Thread den Preis des Produkts(ProductId=2) mit dem Preis des Produkts(ProductId=1).

  • Commit für die Transaktion im zweiten Thread.

Nun sind die Werte von Price(ProductId=1 und ProductId=2) identisch mit dem ursprünglichen Product(ProductId=2).

Für die Momentaufnahmetransaktion werden Threads in der folgenden Reihenfolge ausgeführt:

  • Rufen Sie im ersten Thread den Preis des Produkts (ProductId=2) ab, und speichern Sie sie in der Variablen.

  • Aktualisieren Sie im ersten Thread den Preis des Produkts(ProductId=1) mit dem Preis des Produkts(ProductId=2).

  • Rufen Sie im zweiten Thread den Product-Preis(ProductId=1) aus dem Momentaufnahme ab, und speichern Sie sie in der Variablen.

  • Aktualisieren Sie im zweiten Thread den Preis des Produkts(ProductId=2) mit dem Preis des Produkts(ProductId=1).

  • Commit für die Transaktion im zweiten Thread.

  • Commit für die Transaktion im ersten Thread.

Tauschen Sie nun den Preis der Produkte um(ProductId=1 und ProductId=2).

Die DirtyReadThreads-Klasse zeigt, ob die bestimmte Transaktion das Verhalten dirty Read zulässt. Wenn die Transaktion das Verhalten zulässt, werden die Threads in der folgenden Reihenfolge ausgeführt:

  • Beginnen Sie im ersten Thread eine Transaktion, und fügen Sie den Wert Quantity(ProductId=1) hinzu.

  • Lesen Sie im zweiten Thread den Wert Quantity, und fügen Sie den Wert erneut hinzu.

  • Commit für die Transaktion im zweiten Thread.

  • Führen Sie ein Rollback für die Transaktion im ersten Thread aus.

Wenn die Transaktion das Verhalten zulässt, wird der Quantity-Wert zweimal hinzugefügt.

C#
using System;
using System.Data.SqlClient;
using System.Data;
using System.Threading;
using System.Threading.Tasks;

namespace CSDataIsolationLevel {

   // Use the delegate to call the different threads.
   public delegate void AsyncAccessDatabase(String connString, IsolationLevel level);

   static class DirtyReadThreads {
      public static void DirtyReadFirstThread(String connStrig, IsolationLevel level) {
         Console.WriteLine("Begin the DirtyReadFirstThread.....");

         using (SqlConnection conn = new SqlConnection(connStrig)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    Update dbo.Products set Quantity=Quantity+100 where ProductId=1;
                    WaitFor Delay '00:00:06';";

            conn.Open();

            using (SqlTransaction tran = conn.BeginTransaction(level, "DirtyReadFirst")) {
               using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                  command.Transaction = tran;
                  command.ExecuteNonQuery();
               }

               if (tran != null)
                  tran.Rollback();
            }
         }

         Console.WriteLine("Exit from the DirtyReadFirstThread.....");
      }

      public static void DirtyReadSecondThread(String connStrig, IsolationLevel level) {
         Console.WriteLine("Begin the DirtyReadSecondThread.....");

         using (SqlConnection conn = new SqlConnection(connStrig)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    WaitFor Delay '00:00:03';

                    Declare @qty int;
                    select @qty=Quantity from dbo.Products where ProductId=1;

                    Update dbo.Products set Quantity=@qty+100 where ProductId=1;";

            conn.Open();

            using (SqlTransaction tran = conn.BeginTransaction(level, "DirtyReadSecond")) {
               using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                  command.Transaction = tran;
                  command.ExecuteNonQuery();
               }
               tran.Commit();
            }
         }

         Console.WriteLine("Exit from the DirtyReadSecondThread.....");
      }
   }

   static class NonrepeatableReadThreads {
      public static void NonrepeatableReadFirstThread(String connStrig, IsolationLevel level) {
         Console.WriteLine("Begin the NonrepeatableReadFirstThread.....");

         using (SqlConnection conn = new SqlConnection(connStrig)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    Select ProductId,ProductName,Quantity,Price
                    from dbo.Products
                    where ProductId=1

                    WaitFor Delay '00:00:06';

                    Select ProductId,ProductName,Quantity,Price
                    from dbo.Products
                    where ProductId=1";

            conn.Open();

            using (SqlTransaction tran = conn.BeginTransaction(level, "NonrepeatableReadFirst")) {
               using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                  command.Transaction = tran;

                  using (SqlDataReader reader = command.ExecuteReader()) {
                     Boolean isFirstReader = true;
                     do {
                        Console.WriteLine("It's the result of {0} read:", isFirstReader ? "first" : "second");
                        TransactionIsolationLevels.DisplayData(reader);
                        isFirstReader = !isFirstReader;
                     } while (reader.NextResult() && !isFirstReader);
                  }
               }

               tran.Commit();
            }
         }

         Console.WriteLine("Exit from the NonrepeatableReadFirstThread.....");
      }

      public static void NonrepeatableReadSecondThread(String connStrig, IsolationLevel level) {
         Console.WriteLine("Begin the NonrepeatableReadSecondThread.....");

         using (SqlConnection conn = new SqlConnection(connStrig)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    WaitFor Delay '00:00:03';

                    Update dbo.Products set Quantity=Quantity+100 where ProductId=1;";

            conn.Open();

            using (SqlTransaction tran = conn.BeginTransaction(level, "NonrepeatableReadSecond")) {
               using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                  command.Transaction = tran;
                  command.ExecuteNonQuery();
               }
               tran.Commit();
            }
         }

         Console.WriteLine("Exit from the NonrepeatableReadSecondThread.....");
      }
   }

   static class PhantomReadThreads {
      public static void PhantomReadFirstThread(String connStrig, IsolationLevel level) {
         Console.WriteLine("Begin the PhantomReadFirstThread.....");

         using (SqlConnection conn = new SqlConnection(connStrig)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    Select ProductId,ProductName,Quantity,Price
                    from dbo.Products

                    WaitFor Delay '00:00:06';

                    Select ProductId,ProductName,Quantity,Price
                    from dbo.Products";

            conn.Open();

            using (SqlTransaction tran = conn.BeginTransaction(level, "PhantomReadFirst")) {
               using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                  command.Transaction = tran;

                  using (SqlDataReader reader = command.ExecuteReader()) {
                     Boolean isFirstReader = true;
                     do {
                        Console.WriteLine("It's the result of {0} read:", isFirstReader ? "first" : "second");

                        TransactionIsolationLevels.DisplayData(reader);

                        isFirstReader = !isFirstReader;
                     } while (reader.NextResult() && !isFirstReader);
                  }
               }

               tran.Commit();
            }
         }
         Console.WriteLine("Exit from the PhantomReadFirstThread.....");
      }

      public static void PhantomReadSecondThread(String connStrig, IsolationLevel level) {
         Console.WriteLine("Begin the PhantomReadSecondThread.....");

         using (SqlConnection conn = new SqlConnection(connStrig)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    WaitFor Delay '00:00:03';

                    INSERT [dbo].[Products] ([ProductName], [Quantity], [Price])
                    VALUES (N'White Bike', 843, 1349.00)";

            conn.Open();

            using (SqlTransaction tran = conn.BeginTransaction(level, "PhantomReadSecond")) {
               using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                  command.Transaction = tran;
                  command.ExecuteNonQuery();
               }
               tran.Commit();
            }
         }

         Console.WriteLine("Exit from the PhantomReadSecondThread.....");
      }
   }

   // Demonstrates if the specific transaction allows the following behaviors:
   // 1. Dirty reads;
   // 2. Non-repeatable reads;
   // 3. Phantoms.
   static class TransactionIsolationLevels {
      public static void DemonstrateIsolationLevel(String connString, IsolationLevel level) {
         // Before connect the database, recreate the table.
         OperateDatabase.CreateTable(connString);
         DemonstrateIsolationLevel(connString, level, DirtyReadThreads.DirtyReadFirstThread, DirtyReadThreads.DirtyReadSecondThread);
         DisplayData(connString);
         Console.WriteLine();

         OperateDatabase.CreateTable(connString);
         DemonstrateIsolationLevel(connString, level, NonrepeatableReadThreads.NonrepeatableReadFirstThread, NonrepeatableReadThreads.NonrepeatableReadSecondThread);
         Console.WriteLine();

         OperateDatabase.CreateTable(connString);
         DemonstrateIsolationLevel(connString, level, PhantomReadThreads.PhantomReadFirstThread, PhantomReadThreads.PhantomReadSecondThread);
         Console.WriteLine();
      }

      // Demonstrates if the specific transaction allows the specific behaviors.
      public static void DemonstrateIsolationLevel(String connString, IsolationLevel level,
          AsyncAccessDatabase firstThread, AsyncAccessDatabase secondThread) {
         Task[] tasks ={
                            Task.Factory.StartNew(()=>firstThread(connString, level)),
                            Task.Factory.StartNew(()=>secondThread(connString, level))
                        };

         Task.WaitAll(tasks);
      }

      static class ExchangeValuesThreads {
         public static void ExchangeValuesFirstThread(String connStrig, IsolationLevel level) {
            Console.WriteLine("Begin the ExchangeValuesFirstThread.....");

            using (SqlConnection conn = new SqlConnection(connStrig)) {
               String cmdText = @"Use DbDataIsolationLevel;

                    Declare @price money;
                    select @price=Price from dbo.Products where ProductId=2;

                    Update dbo.Products set Price=@price where ProductId=1;

                    WaitFor Delay '00:00:06'; ";

               conn.Open();
               using (SqlTransaction tran = conn.BeginTransaction(level, "ExchangeValuesFirst")) {

                  using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                     command.Transaction = tran;
                     command.ExecuteNonQuery();
                  }

                  tran.Commit();
               }
            }

            Console.WriteLine("Exit from the ExchangeValuesFirstThread.....");
         }

         public static void ExchangeValuesSecondThread(String connStrig, IsolationLevel level) {
            Console.WriteLine("Begin the ExchangeValuesSecondThread.....");

            using (SqlConnection conn = new SqlConnection(connStrig)) {
               String cmdText = @"Use DbDataIsolationLevel;

                    WaitFor Delay '00:00:03';

                    Declare @price money;
                    select @price=Price from dbo.Products where ProductId=1;

                    Update dbo.Products set Price=@price where ProductId=2;";

               conn.Open();

               using (SqlTransaction tran = conn.BeginTransaction(level, "ExchangeValuesSecond")) {
                  using (SqlCommand command = new SqlCommand(cmdText, conn)) {
                     command.Transaction = tran;
                     command.ExecuteNonQuery();
                  }
                  tran.Commit();
               }
            }

            Console.WriteLine("Exit from the ExchangeValuesSecondThread.....");
         }
      }

      // Demonstrates the difference between the Serializable and Snapshot transaction
      public static void DemonstrateBetweenSnapshotAndSerializable(String connString) {
         OperateDatabase.CreateTable(connString);

         Console.WriteLine("Exchange Vaules in the Snapshot transaction:");
         DemonstrateIsolationLevel(connString, IsolationLevel.Snapshot,
             ExchangeValuesThreads.ExchangeValuesFirstThread,
             ExchangeValuesThreads.ExchangeValuesSecondThread);
         DisplayData(connString);
         Console.WriteLine();

         Console.WriteLine("Cannot Exchange Vaules in the Serializable transaction:");
         OperateDatabase.CreateTable(connString);
         DemonstrateIsolationLevel(connString, IsolationLevel.Serializable,
             ExchangeValuesThreads.ExchangeValuesFirstThread,
             ExchangeValuesThreads.ExchangeValuesSecondThread);
         DisplayData(connString);
      }

      public static void DisplayData(String connString) {
         using (SqlConnection conn = new SqlConnection(connString)) {
            String cmdText = @"Use DbDataIsolationLevel;

                    Select ProductId,ProductName,Quantity,Price
                    from dbo.Products";

            conn.Open();

            using (SqlCommand command = new SqlCommand(cmdText, conn)) {
               using (SqlDataReader reader = command.ExecuteReader()) {
                  DisplayData(reader);
               }
            }
         }
      }

      public static void DisplayData(SqlDataReader reader) {
         Boolean isFirst = true;

         while (reader.Read()) {
            if (isFirst) {
               isFirst = false;

               for (int i = 0; i < reader.FieldCount; i++)
                  Console.Write("{0,-12}   ", reader.GetName(i));
               Console.WriteLine();
            }

            for (int i = 0; i < reader.FieldCount; i++)
               Console.Write("{0,-12}   ", reader[i]);
            Console.WriteLine();
         }
      }
   }

   // This class includes database operations. If there's no database 'DbDataIsolationLevel', create the database.
   static class OperateDatabase {
      public static Boolean CreateDatabase(String connString) {
         using (SqlConnection conn = new SqlConnection(connString)) {
            String cmdText = @"Use Master;

                                     if Db_Id('DbDataIsolationLevel') is null
                                      create Database [DbDataIsolationLevel];";

            using (SqlCommand command = new SqlCommand(cmdText, conn)) {
               conn.Open();
               command.ExecuteNonQuery();
            }

            Console.WriteLine("Create the Database 'DbDataIsolationLevel'");
         }

         return true;
      }

      // If there's no table [dbo].[Products] in DbDataIsolationLevel, create the table; or recreate it.
      public static Boolean CreateTable(String connString) {
         using (SqlConnection conn = new SqlConnection(connString)) {
            String cmdText = @"Use DbDataIsolationLevel

                                    if Object_ID('[dbo].[Products]') is not null
                                    drop table [dbo].[Products]

                                    Create Table [dbo].[Products]
                                    (
                                    [ProductId] int IDENTITY(1,1) primary key,
                                    [ProductName] NVarchar(100) not null,
                                    [Quantity] int null,
                                    [Price] money null
                                    )";

            using (SqlCommand command = new SqlCommand(cmdText, conn)) {
               conn.Open();
               command.ExecuteNonQuery();
            }
         }

         return InsertRows(connString);
      }

      // Insert some rows into [dbo].[Products] table.
      public static Boolean InsertRows(String connString) {
         using (SqlConnection conn = new SqlConnection(connString)) {
            String cmdText = @"Use DbDataIsolationLevel

                    INSERT [dbo].[Products] ([ProductName], [Quantity], [Price]) VALUES (N'Blue Bike', 365,1075.00)
                    INSERT [dbo].[Products] ([ProductName], [Quantity], [Price]) VALUES (N'Red Bike', 159, 1299.00)
                    INSERT [dbo].[Products] ([ProductName], [Quantity], [Price]) VALUES (N'Black Bike', 638, 1159.00)";

            using (SqlCommand command = new SqlCommand(cmdText, conn)) {
               conn.Open();
               command.ExecuteNonQuery();
            }
         }
         return true;
      }

      // Turn on or off 'ALLOW_SNAPSHOT_ISOLATION'
      public static Boolean SetSnapshot(String connString, Boolean isOpen) {
         using (SqlConnection conn = new SqlConnection(connString)) {
            String cmdText = null;

            if (isOpen)
               cmdText = @"ALTER DATABASE DbDataIsolationLevel SET ALLOW_SNAPSHOT_ISOLATION ON";
            else
               cmdText = @"ALTER DATABASE DbDataIsolationLevel SET ALLOW_SNAPSHOT_ISOLATION OFF";

            using (SqlCommand command = new SqlCommand(cmdText, conn)) {
               conn.Open();
               command.ExecuteNonQuery();
            }
         }

         return true;
      }
   }
   class Program {
      static void Main(string[] args) {
         String connString = "Data Source=(local);Initial Catalog=master;Integrated Security=True;Asynchronous Processing=true;";

         OperateDatabase.CreateDatabase(connString);
         Console.WriteLine();

         Console.WriteLine("Demonstrate the ReadUncommitted transaction: ");
         TransactionIsolationLevels.DemonstrateIsolationLevel(connString,
             System.Data.IsolationLevel.ReadUncommitted);
         Console.WriteLine("-----------------------------------------------");

         Console.WriteLine("Demonstrate the ReadCommitted transaction: ");
         TransactionIsolationLevels.DemonstrateIsolationLevel(connString,
             System.Data.IsolationLevel.ReadCommitted);
         Console.WriteLine("-----------------------------------------------");

         Console.WriteLine("Demonstrate the RepeatableRead transaction: ");
         TransactionIsolationLevels.DemonstrateIsolationLevel(connString,
             System.Data.IsolationLevel.RepeatableRead);
         Console.WriteLine("-----------------------------------------------");

         Console.WriteLine("Demonstrate the Serializable transaction: ");
         TransactionIsolationLevels.DemonstrateIsolationLevel(connString,
             System.Data.IsolationLevel.Serializable);
         Console.WriteLine("-----------------------------------------------");

         Console.WriteLine("Demonstrate the Snapshot transaction: ");
         OperateDatabase.SetSnapshot(connString, true);
         TransactionIsolationLevels.DemonstrateIsolationLevel(connString,
             System.Data.IsolationLevel.Snapshot);
         Console.WriteLine("-----------------------------------------------");

         Console.WriteLine("Demonstrate the difference between the Snapshot and Serializable transactions:");
         TransactionIsolationLevels.DemonstrateBetweenSnapshotAndSerializable(connString);
         OperateDatabase.SetSnapshot(connString, false);
         Console.WriteLine();
      }
   }
}

Hinweise

Die IsolationLevel Werte werden von einem .NET-Datenanbieter beim Ausführen einer Transaktion verwendet.

Der IsolationLevel bleibt gültig, bis er explizit geändert wurde, kann aber jederzeit geändert werden. Der neue Wert wird zur Ausführungszeit und nicht zur Analysezeit verwendet. Wenn während einer Transaktion geändert wird, wird das erwartete Verhalten des Servers darin bestehen, die neue Sperrebene auf alle verbleibenden Anweisungen anzuwenden.

Wenn Sie OdbcTransactionverwenden, wird die Transaktion gemäß der vom verwendeten Treiber festgelegten Isolationsstufe ausgeführt, wenn Sie sie nicht festlegen OdbcTransaction.IsolationLevel oder auf Unspecifiedfestlegen.

Gilt für:

Produkt Versionen
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7, 8, 9
.NET Framework 1.1, 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1
.NET Standard 2.0, 2.1
UWP 10.0