2 Stimmen

MSDTC-Ausnahme während der Transaktion: C#

Ich erhalte eine MSDTC-Ausnahme in einer Transaktion in einer C#-Anwendung. Die Funktionalität besteht darin, ein Lakh (hunderttausend) Zipcode-Datensätze in Datenbanktabellen hochzuladen, nachdem sie aus einer CSV-Datei gelesen wurden. Dieser Vorgang wird in etwa 20 Batch-Datenbankoperationen durchgeführt (jeder Batch enthält 5000 Datensätze). Die Funktion funktioniert gut, wenn ich keine Transaktion verwende.

Das Interessante daran ist, dass andere Funktionen, die Transaktionen verwenden, in der Lage sind, ihre Transaktionen abzuschließen. Dies führt mich zu der Annahme, dass die Ausnahmemeldung irreführend ist.

Haben Sie eine Idee, woran das liegen könnte?

Eine Ausnahme: "Der Netzwerkzugriff für Distributed Transaction Manager (MSDTC) wurde deaktiviert. Bitte aktivieren Sie DTC für den Netzwerkzugriff in der Sicherheitskonfiguration für MSDTC mit dem Component Services Administrative Tool."

Quelle: System.Transaktionen

Innere Ausnahme: "Der Transaktionsmanager hat seine Unterstützung für Remote-/Netzwerktransaktionen deaktiviert. (Ausnahme von HRESULT: 0x8004D024)"

Hinweis: Innerhalb der Transaktion gibt es eine for-Schleife. Verursacht sie ein Problem?

Die eigentliche Anforderung lautet: Es gibt einige bestehende Postleitzahlen in der Postleitzahlentabelle. Jeden Monat lädt der Administrator eine neue csv-Datei mit Postleitzahlen hoch. Die neuen Einträge aus der csv-Datei werden eingefügt. Postleitzahlen, die in der csv-Datei nicht vorhanden sind (aber in der Datenbank vorhanden sind), gelten als nicht mehr verfügbar und müssen gelöscht werden. Die Liste der gelöschten Postleitzahlen muss an die Benutzeroberfläche zurückgegeben werden. Die neu hinzugefügten Postleitzahlen müssen ebenfalls zurückgegeben werden.

    private void ProcessZipCodes(StringBuilder dataStringToProcess, int UserID)
    {
        int CountOfUnchangedZipCode = 0;
        string strRetiredZipCode = "";
        string strNewZipCode = "";
        dataStringToProcess.Remove(dataStringToProcess.Length - 1, 1);

        if (dataStringToProcess.Length > 0)
        {

            List<string> batchDataStringList = GetDataStringInBatches(dataStringToProcess);

           //TimeSpan.FromMinutes(0) - to make transaction scope as infinite.
            using (TransactionScope transaction = TransactionScopeFactory.GetTransactionScope(TimeSpan.FromMinutes(0)))
            {

                foreach (string dataString in batchDataStringList)
                {
                    PerformDatabaseOperation(dataString, UserID);
                }

                transaction.Complete();
            }
        }

    }

    private List<string> GetDataStringInBatches(StringBuilder dataStringToProcess)
    {

        List<string> batchDataStringList = new List<string>();
        int loopCounter = 0;
        string currentBatchString = string.Empty;
        int numberOfRecordsinBacth = 5000;
        int sizeOfTheBatch = 0;

        List<string> individualEntriesList = new List<string>();
        string dataString = string.Empty;
        if (dataStringToProcess != null)
        {
            dataString = dataStringToProcess.ToString();
        }
        individualEntriesList.AddRange(dataString.Split(new char[] { '|' }));

        for (loopCounter = 0; loopCounter < individualEntriesList.Count; loopCounter++)
        {

            if (String.IsNullOrEmpty(currentBatchString))
            {
                currentBatchString = System.Convert.ToString(individualEntriesList[loopCounter]);
            }
            else
            {
                currentBatchString = currentBatchString+"|"+System.Convert.ToString(individualEntriesList[loopCounter]);
            }

            sizeOfTheBatch = sizeOfTheBatch + 1;
            if (sizeOfTheBatch == numberOfRecordsinBacth)
            {
                batchDataStringList.Add(currentBatchString);
                sizeOfTheBatch = 0;
                currentBatchString = String.Empty;
            }

        }

        return batchDataStringList;

    }

    private void PerformDatabaseOperation(string dataStringToProcess, int UserID)
    {
        SqlConnection mySqlConnection = new SqlConnection("data source=myServer;initial catalog=myDB; Integrated Security=SSPI; Connection Timeout=0");
        SqlCommand mySqlCommand = new SqlCommand("aspInsertUSAZipCode", mySqlConnection);
        mySqlCommand.CommandType = CommandType.StoredProcedure;
        mySqlCommand.Parameters.Add("@DataRows", dataStringToProcess.ToString());
        mySqlCommand.Parameters.Add("@currDate", DateTime.Now);
        mySqlCommand.Parameters.Add("@userID", UserID);
        mySqlCommand.Parameters.Add("@CountOfUnchangedZipCode", 1000);
        mySqlCommand.CommandTimeout = 0;
        mySqlConnection.Open();
        int numberOfRows = mySqlCommand.ExecuteNonQuery();
    }

Dev Env: Visuelles Studium 2005

Rahmenwerk: .Net 3.0

DB: SQL Server 2005

Wenn ich die Abfrage SELECT [Size],Max_Size,Data_Space_Id,[File_Id],Type_Desc,[Name] FROM MyDB.sys.database_files WHERE data_space_id = 0 ausführe, wird die Größe (des Protokolls) auf 128

UPDATE Wir haben drei verschiedene Datenbanken in unserer Anwendung verwendet. Eine für Daten, eine für den Verlauf und eine für die Protokollierung. Wenn ich enlist = false in den obigen Connectionstring einfüge, funktioniert es vorerst. Aber es ist in meiner Entwicklungsumgebung. Ich bin skeptisch, ob es auch in der Produktion funktionieren wird. Hat jemand eine Idee zu möglichen Risiken?

Danke

Lijo

8voto

Jakob Christensen Punkte 14531

Wenn Sie mehr als eine Verbindung innerhalb einer TransactionScope wird die laufende Transaktion automatisch zu einer verteilten Transaktion eskaliert. Damit verteilte Transaktionen funktionieren, muss der MSDTC sowohl auf dem SQL Server als auch auf dem Rechner, auf dem die Anwendung läuft, so konfiguriert sein, dass ein Netzwerkzugriff möglich ist. SQL Server und der lokale DTC kommunizieren, wenn verteilte Transaktionen ausgeführt werden.

Das Problem in Ihrem Fall ist höchstwahrscheinlich, dass MSDTC auf dem Rechner, auf dem Ihre Anwendung läuft, keinen Netzwerkzugriff zulässt, da dies die Standardeinstellung für Workstations ist. Um dies zu beheben, gehen Sie wie folgt vor:

  1. Gehen Sie zu "Systemsteuerung" -> "Verwaltung" -> "Komponentendienste".
  2. Blättern Sie durch die Baumstruktur, bis Sie zu einem Knoten mit der Bezeichnung "Local DTC" oder so ähnlich gelangen.
  3. Klicken Sie mit der rechten Maustaste und wählen Sie "Eigenschaften".
  4. Gehen Sie zu "Sicherheit" und stellen Sie sicher, dass Sie den Netzwerkzugriff zulassen und auch die ein- und ausgehende Kommunikation mit DTC erlauben.
  5. Klicken Sie auf "Ok".

Wahrscheinlich werden Sie aufgefordert, DTC neu zu starten. Es scheint einen Fehler in der Benutzeroberfläche zu geben, denn obwohl Sie einen Neustart des DTC akzeptieren, wird er nicht neu gestartet. Stattdessen müssen Sie den DTC-Dienst manuell über den Dienstmanager neu starten.

Vergessen Sie übrigens nicht, die Verbindung nach der Verwendung in PerformDatabaseOperation . Es ist gute Praxis, sie in einem using Block:

using (SqlConnection mySqlConnection = new .....)
{
    // Some code here...
    mySqlConnection.Open();
    // Some more code ...
}

1voto

ta.speot.is Punkte 26425

Ist es möglich, dass aspInsertUSAZipCode mit einem verknüpften Server interagiert? Wenn dies der Fall ist, wird er versuchen, Ihre aktuelle lokale Transaktion in eine verteilte Transaktion umzuwandeln (wobei die Integrität der Transaktion zwischen Ihrem Server und dem verknüpften Server gewahrt bleibt).

Es kann notwendig sein, dies außerhalb einer Transaktion zu tun, wenn der MSDTC auf dem entfernten Server nicht für verteilte Transaktionen konfiguriert werden kann. Ich denke, der beste Weg, dies zu tun, ist, eine temporäre Tabelle zu erstellen und dann SqlBulkCopy Ihre Datensätze ein und führen Sie dann aspInsertUSAZipCode unter Verwendung der temporären Tabelle auf dem Server. Möglicherweise müssen Sie einen Cursor verwenden.

Der Zweck der temporären Tabelle besteht darin, dass die Tabelle beim Beenden Ihrer Verbindung entfernt wird, falls etwas schief geht.

0voto

Shiraz Bhaiji Punkte 62129

Wahrscheinlich stoßen Sie auf ein Limit für die maximale Datenmenge in einer Transaktion.

Überprüfen Sie Ihr Ereignisprotokoll auf msdtc-Fehler http://technet.microsoft.com/en-us/library/cc774415(WS.10).aspx

Wenn Sie 100.000 Zeilen in einer einzigen Transaktion haben, werden Sie Probleme bekommen.

Ich glaube nicht, dass eine einzelne Transaktion in diesem Fall funktionieren wird. Sie müssen sich überlegen, warum Sie hier eine Transaktion verwenden, und einen anderen Weg finden, dies zu erreichen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X