通过C#获取创建Excel的错误:转换到COM上下文0x56b098

时间:2016-08-23 21:44:26

标签: c# excel sqldataadapter

我正在尝试通过C#代码创建一个Excel文件,方案是我有一个存储过程,它返回15000条记录,我正在通过SqlDataAdapter读取数据,然后数据填入{{1然后我将数据填充到excel文件中但是一段时间后应用程序抛出以下错误。

错误:

  

为此RuntimeCallableWrapper转换到COM上下文0x56b098失败,并显示以下错误:系统调用失败。 (来自HRESULT的异常:0x80010100(RPC_E_SYS_CALL_FAILED))。这通常是因为创建此RuntimeCallableWrapper的COM上下文0x56b098已断开连接或正忙于执行其他操作。从当前COM上下文释放接口(COM上下文0x56af28)。这可能会导致损坏或数据丢失。要避免此问题,请确保所有COM上下文/公寓/线程都保持活动状态并可用于上下文转换,直到应用程序完全使用表示其中的COM组件的RuntimeCallableWrappers完成。

以下是我正在使用的代码

DataTable

主要方法

public DataTable getData(string query,string year,string month)
{
        SqlDataAdapter da = new SqlDataAdapter();
        DataTable dt = new DataTable();
        SqlCommand cmd = new SqlCommand(query,con);
        cmd.Parameters.AddWithValue("@YR", year);
        cmd.Parameters.AddWithValue("@MN", month);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandTimeout = 0;
        da.SelectCommand = cmd;
        da.Fill(dt);

        return dt;
    }

我需要帮助解决这个问题并期待它。

1 个答案:

答案 0 :(得分:0)

在我看来,通过完全删除数据表可以获得很多效率。数据表很棒,但它们确实有开销,我想知道在数据表中填充15,000行的开销是否会影响您的COM通信。

这是一个避免数据表的解决方案。它预先假定存储过程将按照您希望在Excel中查看它们的顺序转储列。

首先,让您的getData方法返回SqlCommand对象而不是数据表:

public SqlCommand getData(string query, string year, string month)
{
    SqlCommand cmd = new SqlCommand(query, con);
    cmd.Parameters.AddWithValue("@YR", year);
    cmd.Parameters.AddWithValue("@MN", month);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandTimeout = 0;

    return cmd;
}

从这里,您的函数调用和输出到Excel可以看起来像这样:

SqlCommand cmd = func.getData("sp_MRR_Retention_APAC", Yr, mn);

SqlDataReader reader = cmd.ExecuteReader();
for (int col = 0; col < reader.FieldCount; col++)
    xlWorkSheet.Cells[col + 1, 1].Value2 = reader.GetName(col);

while (reader.Read())
{
    for (int col = 0; col < reader.FieldCount; col++)
        if (!reader.IsDBNull(col))
            xlWorkSheet.Cells[row, col + 1] = reader.GetValue(col);
    row++;
}

reader.Close();

您甚至可以将其包装在单个方法中以执行Excel输入:

public void getData(string query, string year, string month, excel.Worksheet Ws)
{
    SqlCommand cmd = new SqlCommand(query, con);
    cmd.Parameters.AddWithValue("@YR", year);
    cmd.Parameters.AddWithValue("@MN", month);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandTimeout = 0;

    int row = 1;

    SqlDataReader reader = cmd.ExecuteReader();
    for (int col = 0; col < reader.FieldCount; col++)
        Ws.Cells[col + 1, 1].Value2 = reader.GetName(col);

    while (reader.Read())
    {
        for (int col = 0; col < reader.FieldCount; col++)
            if (!reader.IsDBNull(col))
                Ws.Cells[row, col + 1] = reader.GetValue(col);
        row++;
    }

    reader.Close();
}

这将简化主方法中的所有代码:

func.getData("sp_MRR_Retention_APAC", Yr, mn, xlWorkSheet);

这是真正的踢球者...... MS Query,内置于Excel中,实际上为您完成了所有这些。通常使用ODBC,但使用MS SQL Server,您可以直接命中服务器而无需ODBC - 这是Microsoft与Microsoft同居的一种优势。老实说,我从未尝试使用存储过程,但通过查询它会很好地工作。我没有理由怀疑MS Query是否可以通过一个程序运行。

C#中的MS Query调用如下所示:

excel.ListObject lo = sheet.ListObjects.AddEx(excel.XlListObjectSourceType.xlSrcQuery,
    connectionString, true, Excel.XlYesNoGuess.xlGuess, range);
lo.QueryTable.CommandText = queryText;
lo.Refresh();

你会发现它非常快 - 我敢说它可以与你手工编写的东西相媲美。