事务期间 MSDTC 异常:C#

发布于 2024-11-16 04:07:20 字数 4103 浏览 4 评论 0原文

我在 C# 应用程序的事务中收到 MSDTC 异常。该功能是在读取 csv 文件后将十万(十万)条邮政编码记录上传到数据库表中。此操作分大约 20 批数据库操作完成(每批包含 5000 条记录)。如果我不使用事务,该功能工作正常。

有趣的是,使用事务的其他功能能够完成其事务。这让我假设异常消息是一个误导性的消息。

对可能出现的问题有什么想法吗?

例外:“分布式事务管理器 (MSDTC) 的网络访问已被禁用。请使用组件服务管理工具在 MSDTC 的安全配置中启用 DTC 进行网络访问。”

来源:System.Transactions

内部异常:“事务管理器已禁用对远程/网络事务的支持。 (HRESULT 异常:0x8004D024)”

注意:事务内部有一个 for 循环。它会引起任何问题吗?

实际需求是:邮政编码表中有一些现有的邮政编码。每个月管理员都会上传新的邮政编码 csv 文件。插入 csv 中的新项目。 csv 中不可用(但存在于数据库中)的邮政编码被视为已停用并被删除。已停用邮政编码的列表将返回到用户界面。新添加的邮政编码也需要返回。

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

开发环境:Visual Studion 2005

框架:.Net 3.0

DB:SQL Server 2005

当我运行查询时 SELECT [Size],Max_Size,Data_Space_Id,[File_Id],Type_Desc,[Name] FROM MyDB.sys.database_files WHERE data_space_id = 0 - - 它说(日志)的大小是 128

更新 我们的应用程序中使用了三个不同的数据库。一种用于数据,一种用于历史,一种用于记录。当我将 enlist = false 放在上面的连接字符串中时,它暂时可以工作。但这是在我的开发环境中。我怀疑它是否也能在生产中发挥作用。对潜在风险有什么想法吗?

谢谢利乔

I am getting a MSDTC exception in a Transaction in a C# application. The functionality is to upload one lakh (one hundred thousand) zipcode records into database tables after reading from a csv file. This operation is done in around 20 batch database operations (each batch containing 5000 records). The functionality is working fine, if I don’t use transaction.

The interesting part is that other functionalities that uses transactions are able to complete their transactions. This leads me to an assumption that the exception message is a misleading one.

Any thoughts on what could be the issue?

Exception: “Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.”

Source: System.Transactions

Inner Exception: “The transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D024)”

Note: There is a for loop inside the transaction. Is it causing any issue?

The actual requirement is: There are some existing zipcodes in zipcode table. Each month the administrator will upload the new zipcode csv file. The new items from csv get inserted. Zipcodes which are not available in csv (but present in database) are considered to be retired and is to be deleted. The list of retired zip codes is to be returned to the User Interface. The newly added zip codes also need to be returned.

    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: Visual Studion 2005

Framework: .Net 3.0

DB: SQL Server 2005

When I run the query SELECT [Size],Max_Size,Data_Space_Id,[File_Id],Type_Desc,[Name] FROM MyDB.sys.database_files WHERE data_space_id = 0 --It says the size (of log) is 128

UPDATE
We have three different databases used in our application. One for data, one for history and one for logging. When I put enlist = false in the above connectionstring, for the time being, it is working. But it is in my development environment. I am skeptic about whether it will work in production also. Any thought on potential risks?

Thanks

Lijo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

千寻… 2024-11-23 04:07:20

当您在 TransactionScope 中打开多个连接时,正在运行的事务将自动升级为分布式事务。为了使分布式事务正常工作,SQL Server 和运行应用程序的计算机上的 MSDTC 必须配置为允许网络访问。 SQL Server 和本地 DTC 在运行分布式事务时进行通信。

您遇到的问题很可能是运行应用程序的计算机上的 MSDTC 不允许网络访问,因为这是工作站的默认设置。要解决此问题,请执行以下操作:

  1. 转到“控制面板”-> “行政”-> “组件服务”。
  2. 浏览树,直到到达名为“本地 DTC”或类似名称的节点。
  3. 右键单击并选择“属性”。
  4. 转到“安全”并确保允许网络访问并允许与 DTC 的入站和出站通信。
  5. 单击“确定”。

系统可能会提示您重新启动 DTC。 UI 中似乎存在错误,因为即使您接受 DTC 重新启动,它也不会重新启动。相反,您必须在服务管理器中手动重新启动 DTC 服务。

顺便说一句,请记住在 PerformDatabaseOperation 中使用后关闭连接。最好将其放在 using 块中:

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

When you are opening more than one connection within a TransactionScope, the running transaction will automatically be escalated to a distributed transaction. For distributed transactions to work, the MSDTC on both SQL Server and the machine running the application must be configured to allow network access. SQL Server and the local DTC communicate when running distributed transactions.

The problem in your case is most likely that MSDTC on the machine running your application does not allow network access because this is the default for workstations. To fix this do the following:

  1. Go to "Control Panel" -> "Aministration" -> "Component Services".
  2. Browse through the tree until you get to a node called "Local DTC" or something like that.
  3. Right-click and choose "Properties".
  4. Go to "Security" and make sure that you allow network access and also allow inbound and outbound communication with DTC.
  5. Click "Ok".

You will probably be prompted to restart DTC. There seems to be a bug in the UI because even though you accept a restart of the DTC, it will not be restarted. Instead you have to restart the DTC service manually in the service manager.

BTW, remember to close the connection after use in PerformDatabaseOperation. It is good practice to put it in a using block:

using (SqlConnection mySqlConnection = new .....)
{
    // Some code here...
    mySqlConnection.Open();
    // Some more code ...
}
赴月观长安 2024-11-23 04:07:20

aspInsertUSAZipCode 是否可以与链接服务器交互?如果是这样,它将尝试将您当前的本地事务提升为分布式事务(在您的服务器和链接服务器之间保留事务完整性)。

如果无法为分布式事务配置远程服务器上的 MSDTC,则可能需要在事务外部执行此操作。我认为最好的方法是创建一个临时表,然后将记录SqlBulkCopy放入其中,然后使用服务器上的临时表执行aspInsertUSAZipCode。您可能需要使用光标。

临时表的目的是,如果出现问题,该表将在连接终止时被删除。

Is it possible that aspInsertUSAZipCode interacts with a linked server? If it does then it will try and promote your current local transaction to a distributed transaction (with transactional integrity preserved between your server and the linked server).

It may be necessary to do this outside of a transaction, if the MSDTC on the remote server cannot be configured for distributed transactions. I think the best way to do this is to create a temporary table and then SqlBulkCopy your records into it and then execute aspInsertUSAZipCode using the temporary table on the server. You may need to use a cursor.

The purpose of the temporary table is so that if something goes wrong, the table is removed at the termination of your connection.

﹂绝世的画 2024-11-23 04:07:20

您可能会达到事务限制中的最大数据量。

检查事件日志中是否有任何 msdtc 错误 http://technet. microsoft.com/en-us/library/cc774415(WS.10).aspx

在单个事务中包含 100,000 行会给您带来问题。

我认为单个事务在这种情况下不起作用,您需要看看为什么在这里使用事务,并找到不同的方法来完成它。

You are probably hitting some max amount of data in a transaction limit.

Check your event log for any msdtc errors http://technet.microsoft.com/en-us/library/cc774415(WS.10).aspx

Having 100,000 rows in a single transaction is going to give you problems.

I do not think that a single transaction is going to work in this case, you need to look at why you are using a transaction here, and find a different way to accomplish it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文