如何同时执行 DataAdapter.Fill()
Posted
技术标签:
【中文标题】如何同时执行 DataAdapter.Fill()【英文标题】:How to execute DataAdapter.Fill() simultaneously 【发布时间】:2020-03-05 07:15:50 【问题描述】:我已经在一个 ASP.Net 应用程序上工作了很长时间,并且有超过 10 个客户端在使用该应用程序。但是现在我在应用程序中发现了一个问题,就是我有一个存储过程调用,大约需要30秒才能执行。这不是问题,因为 SQL 代码高度复杂且循环多次。问题是 : 每当执行该存储过程调用时,我都无法使用任何其他函数或存储过程调用。 当我尝试调试时,问题是“DataAdapter.Fill()”函数正在等待第一个存储过程调用完成。
我执行存储过程调用并返回数据的代码是:
public static DataSet ExecuteQuery_SP(string ProcedureName, object[,] ParamArray)
SqlDataAdapter DataAdapter = new SqlDataAdapter();
DataSet DS = new DataSet();
try
if (CON.State != ConnectionState.Open)
OpenConnection();
SqlCommand cmd = new SqlCommand();
cmd.CommandTimeout = 0;
cmd.CommandText = ProcedureName;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = CON;
cmd.Transaction = SqlTrans;
string ParamName;
object ParamValue;
for (int i = 0; i < ParamArray.Length / 2; i++)
ParamName = ParamArray[i, 0].ToString();
ParamValue = ParamArray[i, 1];
cmd.Parameters.AddWithValue(ParamName, ParamValue);
DataAdapter = new SqlDataAdapter(cmd);
DataAdapter.Fill(DS);
cmd.CommandText = "";
catch (Exception ea)
return DS;
所有存储过程调用都通过此函数进行。因此,当我的第一个存储过程调用“A”正在运行时,存储过程调用“B”在“A”完成之前不会执行。
这会降低应用程序的整体性能并导致数据检索出现问题。 我浏览了谷歌,发现“线程”可能会有所帮助,但我无法正确执行线程。我对这类事情不是很熟悉。如果您可以纠正问题,这将很有帮助。 我的第一个存储过程调用是:
ds = DB.ExecuteQuery_SP("SelectOutstandingReportDetailed", parArray);
其中 ds 是 DataSet 对象。 第二个存储过程调用是:
ds = DB.ExecuteQuery_SP("[SelectAccLedgersDetailsByID]", ParamArray);
我当前的数据库连接打开功能是:
public static bool OpenConnection()
try
Server = (String)HttpContext.GetGlobalResourceObject("Resource", "Server");
DBName = (String)HttpContext.GetGlobalResourceObject("Resource", "DBName");
UserName = (String)HttpContext.GetGlobalResourceObject("Resource", "UserName");
PassWord = (String)HttpContext.GetGlobalResourceObject("Resource", "PassWord");
string ConnectionString;
ConnectionString = "server=" + Server + "; database=" + DBName + "; uid=" + UserName + "; pwd=" + PassWord + "; Pooling='true';Max Pool Size=100;MultipleActiveResultSets=true;Asynchronous Processing=true";
CON.ConnectionString = ConnectionString;
if (CON.State != ConnectionState.Open)
CON.Close();
CON.Open();
catch (Exception ea)
return false;
其中 'CON' 是一个公共 SqlConnection 变量
static SqlConnection CON = new SqlConnection();
我发现了问题,就是所有的存储过程调用都是通过这个'CON'对象来执行的。如果每个存储过程调用都有单独的 SqlConnection 对象,则没有问题。 那么是否可以为每个 ExecuteQuery_SP 调用创建单独的 SqlConnection 。 如果有任何疑问,请发表评论。 谢谢你
【问题讨论】:
"所有存储过程调用都通过这个函数工作。"那不要用同一种方法来执行不同的存储过程? ExecuteQuery_SP 函数是打开数据库连接的常用函数。我不能为每个存储过程调用提供单独的函数,因为将有大约一百个存储过程调用@IrishChieftain docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/… -- 你试过这个吗?抱歉,一开始语言错误。让我看看 C# 是否有类似的东西。 这就是所谓的数据层。您有不同的数据访问方法,每种方法都可能调用存储过程。试一试。 您是否使用单个SqlConnection
进行所有数据访问?你不应该那样做。为每个请求创建一个新的SqlConnection
。 ADO.NET 在内部管理 connection pool。
【参考方案1】:
默认情况下,SQL Server 将同时允许thousands of connections。这不是您问题的根源。您已强制对存储过程的每次调用都通过一个方法进行汇集。排除您对存储过程的调用 - 换句话说,丢失 ExecuteQuery_SP 方法,这是一个瓶颈。然后再次测试。
这里是data layers的介绍。
【讨论】:
我没有正确理解你。您的意思是为每个存储过程调用编写单独的类似 ExecuteQuery_SP 的函数吗? 是的。您正在不必要地制造瓶颈。 对不起,在我的情况下这是不可能的。因为有大量的存储过程调用。 我尝试为“SelectOutstandingReportDetailed”添加单独的函数,但问题仍然存在。 您只是调用了“SelectOutstandingReportDetailed”而不是其他任何存储过程吗?有很多连接的查询有问题吗?也许缺少数据库索引?您需要缩小范围。【参考方案2】:这是我可以为您创建的最简单的版本。 重要提示:要了解您应该了解 async-await。
您可以从Microsoft C# Async-Await Docs开始
// TODO set up your connection string
private string connectionString = "<your connection string>";
// Gets data assyncronously
public static async Task<DataTable> GetDataAsync(string procedureName, object[,] ParamArray)
try
var asyncConnectionString = new SqlConnectionStringBuilder(connectionString)
AsynchronousProcessing = true
.ToString();
using (var conn = new SqlConnection(asyncConnectionString))
using (var SqlCommand = new SqlCommand())
SqlCommand.Connection = conn;
SqlCommand.CommandText = procedureName;
SqlCommand.CommandType = CommandType.StoredProcedure;
string ParamName;
object ParamValue;
for (int i = 0; i < ParamArray.Length / 2; i++)
ParamName = ParamArray[i, 0].ToString();
ParamValue = ParamArray[i, 1];
SqlCommand.Parameters.AddWithValue(ParamName, ParamValue);
conn.Open();
var data = new DataTable();
data.BeginLoadData();
using (var reader = await SqlCommand.ExecuteReaderAsync().ConfigureAwait(true))
if (reader.HasRows)
data.Load(reader);
data.EndLoadData();
return data;
catch (Exception Ex)
// Log error or something else
throw;
public static async Task<DataTable> GetData(object General, object Type, string FromDate, string ToDate)
object[,] parArray = new object[,]
"@BranchID",General.BranchID,
"@FinancialYearID",General.FinancialYearID,
"@Type",Type,
"@FromDate",DateTime.ParseExact(FromDate, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture),
"@ToDate",DateTime.ParseExact(ToDate, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture)
;
return await DataBaseHelper.GetDataAsync("SelectOutstandingReportDetailed", parArray);
// Calls database assyncronously
private async Task ConsumeData()
DataTable dt = null;
try
// TODO configure your parameters here
object general = null;
object type = null;
string fromDate = "";
string toDate = "";
dt = await GetData(general, type, fromDate, toDate);
catch (Exception Ex)
// do something if an error occurs
System.Diagnostics.Debug.WriteLine("Error occurred: " + Ex.ToString());
return;
foreach (DataRow dr in dt.Rows)
System.Diagnostics.Debug.WriteLine(dr.ToString());
// Fired when some button is clicked. Get and use the data assyncronously, i.e. without blocking the UI.
private async void button1_Click(object sender, EventArgs e)
await ConsumeData();
【讨论】:
我会尝试这个,但对我来说它看起来非常复杂,因为我对此一点也不熟悉。我会尽力做到这一点。如果可以的话请帮我写代码 “SelectOutstandingReportDetailed”的参数是什么?如果你给我,我可以告诉你我的方法。 而且,我很好奇。如何创建 ParamArray 变量?它像 [ ["@varname",value], ["@varname2",value] ] 吗?我不明白。 object[,] parArray = new object[,] "@BranchID",General.BranchID, "@FinancialYearID",General.FinancialYearID, "@Type",Type , "@FromDate",DateTime.ParseExact(FromDate, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture), "@ToDate",DateTime.ParseExact(ToDate, "dd/MM/yyyy" , System.Globalization.CultureInfo.InvariantCulture) ;这是 SelectOutstandingReportDetailed 的 paramarray 对象 DataTable.Load 方法是同步的。因此,代码中的主要操作 - 加载数据 - 同步发生。以上是关于如何同时执行 DataAdapter.Fill()的主要内容,如果未能解决你的问题,请参考以下文章
使用 C# dataAdapter.Fill() 和 dataAdapter.Update() 将表的数据从一个数据库传输到另一个数据库的同一个表