调用 ExecuteReader 时从 DAL 基类处置 SqlConnection
我被分配到一个项目,其中 DAL 由一个基类组成,该基类具有返回 IDataReader、一个对象(int、字符串等)或一个 DataSet 的函数。 还存在 ExecuteNonQuery 函数。 该DAL仅访问USP(SQL Server),并使用MS的SqlHelper来执行查询。 以下是来自基类的两个示例函数:
protected IDataReader ExecuteReader(string storedProcedure, params object[] parameterValues)
{
SqlConnection HConnection = new SqlConnection(myConnString);
IDataReader ret = null;
try
{
ret = SqlHelper.ExecuteReader(HConnection, storedProcedure, parameterValues);
}
catch (Exception ex)
{
HanldeError(ex, storedProcedure, parameterValues);
}
return ret;
}
protected object ExecuteScalar(string storedProcedure, params object[] parameterValues)
{
using (SqlConnection HConnection = new SqlConnection(myConnString))
{
object ret = null;
try
{
ret = SqlHelper.ExecuteScalar(HConnection, storedProcedure, parameterValues);
}
catch (Exception ex)
{
HanldeError(ex, storedProcedure, parameterValues);
}
return ret;
}
}
其他类从该基类派生,创建特定于任务的 DAL 类,例如:
public class Orders : BaseDal {
public IDataReader GetOrdersList(int clientId, int agentId)
{
return ExecuteReader("usp_Orders_GetOrdersList", clientId, agentId);
}
...
}
然后是调用 DAL 函数并用数据填充对象(例如 Order 对象)的 BLL 类基于从 IDataReader 对象读取的数据:
public Order[] GetOrdersList(int ClientIDX, int AgentIDX)
{
List<Order> ret = null;
using (IDataReader dr = objDAL.GetOrdersList(ClientIDX, AgentIDX))
{
if (dr != null)
{
ret = new List<Order>();
while (dr.Read())
{
ret.Add(xReadOrder(dr, 0));
}
}
}
return ret.ToArray();
}
我的问题是这样的 - 如果您查看从 BaseDal 获取的代码,您会注意到只有 ExecuteScalar 实际上终止了 SqlConnection 对象(using 语句) - 所有情况都是如此我在那里的职能。 使用 ExecuteReader 我无法做到这一点,因为我返回一个打开的 SqlDataReader 对象并关闭连接将使读取器无效。 我从 DAL 获取和使用 IDataReader 的所有代码都使用 using 语句,但是 SqlConnection 对象是否也被释放,或者是否在稍后阶段被 GC 处理,不尽快释放它会损害连接池? 如果是这样,该如何治疗?
另外,是否有比上述方法更好的创建 DAL 的方法? 我不太关心与数据存储无关的 DAL,我们只需要一个可靠且易于维护的 DAL,一个可以从多个线程获取多个并发连接的 DAL。
预先感谢您对此的任何帮助。
I've been assigned on a project where the DAL consists of a base class with functions to return IDataReader, an Object (int, string and the like), or a DataSet. An ExecuteNonQuery function also exists. This DAL only accesses USPs (SQL Server), and is using MS's SqlHelper to execute the queries. Here's two sample functions from the base:
protected IDataReader ExecuteReader(string storedProcedure, params object[] parameterValues)
{
SqlConnection HConnection = new SqlConnection(myConnString);
IDataReader ret = null;
try
{
ret = SqlHelper.ExecuteReader(HConnection, storedProcedure, parameterValues);
}
catch (Exception ex)
{
HanldeError(ex, storedProcedure, parameterValues);
}
return ret;
}
protected object ExecuteScalar(string storedProcedure, params object[] parameterValues)
{
using (SqlConnection HConnection = new SqlConnection(myConnString))
{
object ret = null;
try
{
ret = SqlHelper.ExecuteScalar(HConnection, storedProcedure, parameterValues);
}
catch (Exception ex)
{
HanldeError(ex, storedProcedure, parameterValues);
}
return ret;
}
}
Other classes derive from this base class, creating task-specific DAL classes, for instance:
public class Orders : BaseDal {
public IDataReader GetOrdersList(int clientId, int agentId)
{
return ExecuteReader("usp_Orders_GetOrdersList", clientId, agentId);
}
...
}
Then there are BLL classes that call the DAL functions, and fill objects (for example an Order object) with the data based on the data read from the IDataReader object:
public Order[] GetOrdersList(int ClientIDX, int AgentIDX)
{
List<Order> ret = null;
using (IDataReader dr = objDAL.GetOrdersList(ClientIDX, AgentIDX))
{
if (dr != null)
{
ret = new List<Order>();
while (dr.Read())
{
ret.Add(xReadOrder(dr, 0));
}
}
}
return ret.ToArray();
}
My question is this - if you'll look at the code taken from BaseDal, you'll notice only ExecuteScalar actually terminates the SqlConnection object (the using statement) - this is the case with all my functions there. With ExecuteReader I cannot do that, as I'm returning an open SqlDataReader object and closing the connection will invalidate the reader.
I have all the code getting and using an IDataReader from the DAL use the using statement, but is the SqlConnection object being disposed as well, or is it being GC'ed at a later stage, hurting the connection pooling by not freeing it up sooner? If so, how can this be treated?
Also, is there a better approach to creating a DAL than the one described above? I'm less concerned about data store agnostic DAL, we only need a solid and easy to maintain one, one that can take many concurrent connections from many threads.
Thanks in advance for any help on this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您在执行命令时指定
CommandBehavior.CloseConnection
,SqlDataReader
将关闭连接。 不过,总感觉有点丑。相反,将
SqlConnection
传递到方法中并使用它。 然后调用者可以控制何时处理它。 或者,采用Action
与打开的读取器一起执行,并在执行操作后使该方法关闭读取器和连接。 当然,该行动必须为读者做一切需要做的事情。SqlDataReader
will close the connection if you specifyCommandBehavior.CloseConnection
when you execute the command. However, it feels a bit ugly anyway.Instead, pass a
SqlConnection
into the method and use that. Then the caller has control over when that's disposed. Alternatively, take anAction<SqlDataReader>
to execute with the open reader, and make the method close both the reader and the connection, after executing the action. That action would have to then do everything it needed to with the reader, of course.