c#Win表单-CLR无法从COM上下文转换
SQL版本:SQL Server 2008 R2标准版c#Win表单-CLR无法从COM上下文转换,c#,.net,sql-server,multithreading,c#-3.0,C#,.net,Sql Server,Multithreading,C# 3.0,SQL版本:SQL Server 2008 R2标准版 应用程序:.Net 3.5(Windows窗体) 这是我在运行代码后收到的错误 CLR已经60秒无法从COM上下文0xe88270转换到COM上下文0xe88328。拥有目标上下文/单元的线程很可能正在执行非泵送等待或在不泵送Windows消息的情况下处理长时间运行的操作。这种情况通常会对性能产生负面影响,甚至可能导致应用程序变得无响应或内存使用量随时间不断累积。为避免此问题,所有单线程单元(STA)线程都应使用泵送等待原语(如CoWait
应用程序:.Net 3.5(Windows窗体) 这是我在运行代码后收到的错误 CLR已经60秒无法从COM上下文0xe88270转换到COM上下文0xe88328。拥有目标上下文/单元的线程很可能正在执行非泵送等待或在不泵送Windows消息的情况下处理长时间运行的操作。这种情况通常会对性能产生负面影响,甚至可能导致应用程序变得无响应或内存使用量随时间不断累积。为避免此问题,所有单线程单元(STA)线程都应使用泵送等待原语(如CoWaitForMultipleHandles),并在长时间运行操作期间定期泵送消息 以下代码有时会产生上述错误。很可能在插入24000多条记录后,会出现错误 准则的目标
编写代码是为了插入虚拟条目来测试我的应用程序
Random rnd = new Random();
string Data = "";
for (int i = 0; i < 2000000; i++)
{
Data = "Insert Into Table1(Field1)values('" + rnd.Next(0, 200000000) + "');" + Environment.NewLine +
"Insert Into Table1(Field1)values('" + rnd.Next(0, 200000000) + "');" + Environment.NewLine +
"Insert Into Table1(Field1)values('" + rnd.Next(0, 200000000) + "');" + Environment.NewLine +
"Insert Into Table1(Field1)values('" + rnd.Next(0, 200000000) + "');" + Environment.NewLine +
"Insert Into Table1(Field1)values('" + rnd.Next(0, 200000000) + "');" + Environment.NewLine +
"Insert Into Table1(Field1)values('" + rnd.Next(0, 200000000) + "');";
ExecuteQuery(Data);//Error is displayed here
}
注意:-我在查询中编写了多个完全相同的insert语句,因为这将减少SQL必须处理的查询数量
问题如何优化代码以防止错误发生 “CLR已经60秒无法从COM上下文…转换到COM上下文…了。拥有目标上下文/单元的线程很可能正在进行非泵送等待,或者在不泵送Windows消息的情况下处理长时间运行的操作。” 您的问题并不完全清楚您的代码是如何详细工作的,因此我将假设您的应用程序不是多线程的,即执行2×600万数据库
INSERT
s的循环在应用程序的主(UI)线程上运行。这可能需要一段时间(>60秒),因此您的UI在此期间将冻结(即使其无响应)。这是因为当您的(阻塞)循环仍在运行时,Windows窗体永远没有机会运行并对用户输入作出反应。我打赌这就是你所引用的警告的原因
请改用参数化SQL命令。
您可以做的第一件简单的事情是将SqlCommand
转换为参数化的命令。然后,您将发出具有相同SQL文本的命令;只有单独提供的参数会有所不同:
private void InsertRandomNumbers(int[] randomNumbers)
{
const string commandText = "INSERT INTO dbo.Table1 (Field1) VALUES (@field1);"
using (var connection = new SqlConnection(connectionString)
using (var command = new SqlCommand(commandText, connection))
{
var field1Parameter = new SqlDataParameter("@field1", SqlDbType.Int);
command.Parameters.Add(field1Parameter);
connection.Open();
foreach (int randomNumber in randomNumbers)
{
field1Parameter.Value = randomNumber;
/* int rowsAffected = */ command.ExecuteNonQuery();
}
connection.Close();
}
}
注意commandText
是如何定义为常量的。这很好地表明,SQL Server也会将其识别为始终相同的命令—对于参数化命令,实际参数值是单独提供的—SQL Server只会编译和优化语句一次(并将编译后的语句放入其缓存中,以便随后可以重用)而不是一次又一次地做同样的事情。仅此一项就可以节省大量时间
长时间运行的操作应该是异步的,这样您的UI就不会冻结。
您可以做的另一件事是将数据库代码转移到后台线程,这样应用程序的UI就不会冻结
假设数据库INSERT
循环当前由按钮doWorkButton
触发。因此,for的循环位于按钮Click
事件处理程序中:
private void doWorkButton_Click(object sender, EventArgs e)
{
…
for (i = 0; i < 2000000; i++)
{
Data = …
ExecuteQuery(Data);
// note: I leave it as an exercise to you to combine the
// above suggestion (parameterized queries) with this one.
}
}
就这样。现在我们需要修改handler方法,使其也是异步的:
private async void doWorkButton_Click(object sender, EventArgs e)
{
try
{
…
for (i = 0; i < 2000000; i++) { … }
{
Data = …
await ExecuteNonQueryAsync(Data);
}
}
catch
{
… // do not let any exceptions escape this handler method
}
}
我现在不能测试,但我希望这能让你开始。如果您想进一步改进基于SqlBulkCopy
的解决方案,我建议您创建一个IDataReader
的自定义实现,该实现动态创建包含随机数据的“行”,然后将该行的实例传递给SqlBulkCopy
,而不是预填充的数据表。这意味着您不必在内存中保存一个庞大的数据表,而是一次只保留一行数据。使用后台线程,在SQL查询中执行所有这些操作,不在c#code@viveknuna我希望它能尽快工作,我不介意在屏幕前等待,这就是为什么我使用主线程来做这件事。尝试在运行执行计划时在sql server中运行查询,查看您的查询占用的时间不要太多使用索引,检查是否有锁可以使用SqlBulkCopy
private async Task ExecuteNonQueryAsync(string commandText)
{
using (var connection = new SqlConnection(connectionString)
using (var command = new SqlCommand(commandText, connection))
{
connection.Open();
/* int rowsAffected = */ await command.ExecuteNonQueryAsync();
connection.Close();
}
}
private async void doWorkButton_Click(object sender, EventArgs e)
{
try
{
…
for (i = 0; i < 2000000; i++) { … }
{
Data = …
await ExecuteNonQueryAsync(Data);
}
}
catch
{
… // do not let any exceptions escape this handler method
}
}
private async void doWorkButton_Click(object sender, EventArgs e)
{
// Prepare the data to be loaded into your database table.
// Note, this could be done more efficiently.
var dataTable = new DataTable();
{
dataTable.Columns.Add("Field1", typeof(int));
var rnd = new Random();
for (int i = 0; i < 12000000; ++i)
{
dataTable.Rows.Add(rnd.Next(0, 2000000));
}
}
using (var connection = new SqlConnection(connectionString))
using (var bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "dbo.Table1";
try
{
// This will perform a bulk insert into the table
// mentioned above, using the data passed in as a parameter.
await bulkCopy.WriteToServerAsync(dataTable);
}
catch
{
Console.WriteLine(ex.Message);
}
}
}