C# - 应用程序中的 DataTable 内存不足异常以捕获 SQL 服务器 "INSERT" 事件
C# - DataTable Out of Memory exception in application to catch SQL Server "INSERT" events
我的任务是创建一个应用程序来监视特定 table 上的任何 "INSERT" 事件。我打算使用 SqlDependency 在数据库和 C# 应用程序之间创建一个通知 link,但由于安全问题我无法做到这一点。
因此,我将我的应用程序建模如下:
这很好,但事实证明,我正在查询的 SQL table 的大小相当大。 table 有近 350 万行 55 列。加载到 C# DataTable 对象时,出现内存不足异常。
internal static DataTable ExecuteQuery(string query, Dictionary<string,string> parameters = null)
{
try
{
using (SqlConnection dbconn = new SqlConnection(SQLServer.Settings.ConnectionString))
using (SqlCommand cmd = new SqlCommand())
{
dbconn.Open(); // Open the connection
cmd.CommandText = query; // Set the query text
cmd.Connection = dbconn;
if (parameters != null)
{
foreach (var parameter in parameters) // Add filter parameters
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
var dt = new DataTable();
using (SqlDataAdapter adpt = new SqlDataAdapter(cmd)){adpt.Fill(dt);} // MY ERROR OCCURS HERE!
dbconn.Close();
queryError = false;
return dt;
}
}
catch(Exception ex)
{
queryError = true;
EventLogger.WriteToLog("ExecuteQuery()", "Application", "Error: An error has occured while performing a database query.\r\nException: " + ex.Message);
return null;
}
}
当 运行 上面的代码时,我在 SqlDataAdapter.Fill(dt)
行出现以下错误
Exception of type 'System.OutOfMemoryException' was thrown.
有没有一种方法可以重构我的应用程序或防止 DataTable class 的这种令人难以置信的高内存消耗? SQL 服务器似乎有能力从 table 执行 select * 但是当我用相同的数据填充 DataTable 时,我用掉了超过 6GB 的 RAM!为什么使用 DataTable 时开销如此之大?
这是我 flowchart 的 link。
我使用 SqlDataReader
class 解决了这个问题。 class 允许您 "stream" 逐行 sql 结果集,而不是一次返回整个结果集并将其加载到内存中。
现在在流程图的第 5 步中,我只能查询第一行。然后在第 6 步中,我可以稍后再次查询并一次一行地遍历新结果集,直到找到我开始的原始行。一直以来,我都在用新结果填充数据表。这完成了两件事。
- 我不需要一次将查询中的所有数据加载到本地内存中。
- 我可以立即得到 "inverse" 数据集。又名......我可以得到我第一次检查时不存在的新插入的行。
这正是我所追求的。这里只是代码的一部分:
private static SqlDataReader reader;
private static SqlConnection dbconn = new SqlConnection(SQLServer.Settings.ConnectionString);
private void GetNextRows(int numRows)
{
if (dbconn.State != ConnectionState.Open)
OpenConnection();
// Iterate columns one by one for the specified limit.
int rowCnt = 0;
while (rowCnt < numRows)
{
while (reader.Read())
{
object[] row = new object[reader.FieldCount];
reader.GetValues(row);
resultsTable.LoadDataRow(row, LoadOption.PreserveChanges);
rowCnt++;
sessionRowPosition++;
break;
}
}
}
整个 class 对我来说太大了 post 但其中一个警告是我的检查间隔很长,大约几天,所以我需要关闭检查之间的连接。当关闭与 SqlDataReader 的连接时,您丢失了行位置,因此我需要添加一个计数器来跟踪它。
检查您是否查询 select。您可能从数据库中获取了很多行。
我的任务是创建一个应用程序来监视特定 table 上的任何 "INSERT" 事件。我打算使用 SqlDependency 在数据库和 C# 应用程序之间创建一个通知 link,但由于安全问题我无法做到这一点。
因此,我将我的应用程序建模如下:
这很好,但事实证明,我正在查询的 SQL table 的大小相当大。 table 有近 350 万行 55 列。加载到 C# DataTable 对象时,出现内存不足异常。
internal static DataTable ExecuteQuery(string query, Dictionary<string,string> parameters = null)
{
try
{
using (SqlConnection dbconn = new SqlConnection(SQLServer.Settings.ConnectionString))
using (SqlCommand cmd = new SqlCommand())
{
dbconn.Open(); // Open the connection
cmd.CommandText = query; // Set the query text
cmd.Connection = dbconn;
if (parameters != null)
{
foreach (var parameter in parameters) // Add filter parameters
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
var dt = new DataTable();
using (SqlDataAdapter adpt = new SqlDataAdapter(cmd)){adpt.Fill(dt);} // MY ERROR OCCURS HERE!
dbconn.Close();
queryError = false;
return dt;
}
}
catch(Exception ex)
{
queryError = true;
EventLogger.WriteToLog("ExecuteQuery()", "Application", "Error: An error has occured while performing a database query.\r\nException: " + ex.Message);
return null;
}
}
当 运行 上面的代码时,我在 SqlDataAdapter.Fill(dt)
Exception of type 'System.OutOfMemoryException' was thrown.
有没有一种方法可以重构我的应用程序或防止 DataTable class 的这种令人难以置信的高内存消耗? SQL 服务器似乎有能力从 table 执行 select * 但是当我用相同的数据填充 DataTable 时,我用掉了超过 6GB 的 RAM!为什么使用 DataTable 时开销如此之大?
这是我 flowchart 的 link。
我使用 SqlDataReader
class 解决了这个问题。 class 允许您 "stream" 逐行 sql 结果集,而不是一次返回整个结果集并将其加载到内存中。
现在在流程图的第 5 步中,我只能查询第一行。然后在第 6 步中,我可以稍后再次查询并一次一行地遍历新结果集,直到找到我开始的原始行。一直以来,我都在用新结果填充数据表。这完成了两件事。
- 我不需要一次将查询中的所有数据加载到本地内存中。
- 我可以立即得到 "inverse" 数据集。又名......我可以得到我第一次检查时不存在的新插入的行。
这正是我所追求的。这里只是代码的一部分:
private static SqlDataReader reader;
private static SqlConnection dbconn = new SqlConnection(SQLServer.Settings.ConnectionString);
private void GetNextRows(int numRows)
{
if (dbconn.State != ConnectionState.Open)
OpenConnection();
// Iterate columns one by one for the specified limit.
int rowCnt = 0;
while (rowCnt < numRows)
{
while (reader.Read())
{
object[] row = new object[reader.FieldCount];
reader.GetValues(row);
resultsTable.LoadDataRow(row, LoadOption.PreserveChanges);
rowCnt++;
sessionRowPosition++;
break;
}
}
}
整个 class 对我来说太大了 post 但其中一个警告是我的检查间隔很长,大约几天,所以我需要关闭检查之间的连接。当关闭与 SqlDataReader 的连接时,您丢失了行位置,因此我需要添加一个计数器来跟踪它。
检查您是否查询 select。您可能从数据库中获取了很多行。