Я был назначен в проекте, где DAL состоит из базового класса с функциями для возврата IDataReader, Object (int, string и т.п.) или DataSet. Также существует функция ExecuteNonQuery. Этот DAL только обращается к USPs (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);
}
...
}
Затем существуют классы BLL, которые вызывают функции DAL, и заполняют объекты (например, объект Order) данными на основе данных, считываемых из объекта 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 и закрытие соединения приведет к недействительности читателя. У меня есть весь код, получающий и использующий IDataReader из DAL, используя инструкцию using, но также и объект SqlConnection, или он GC на поздней стадии, ущемляя объединение пулов, не высвобождая его раньше? Если да, то как это можно лечить?
Кроме того, существует ли лучший подход к созданию DAL, чем описанный выше? Я меньше беспокоюсь об агностическом хранилище данных DAL, нам нужен только прочный и простой в обслуживании, который может принимать много параллельных соединений из многих потоков.
Заранее благодарим за помощь.
SqlDataReader
закроет соединение, если вы укажете CommandBehavior.CloseConnection
при выполнении команды. Тем не менее, он чувствует себя немного уродливым в любом случае.
Вместо этого передайте a SqlConnection
в метод и используйте это. Тогда вызывающий имеет контроль над тем, когда он расположен. В качестве альтернативы, выполните Action<SqlDataReader>
для выполнения с открытым считывателем и сделайте метод закрытым как для чтения, так и для соединения после выполнения действия. Разумеется, это действие должно было бы сделать все, что нужно для читателя.
CommandBehavior.CloseConnection
кажется мне лучшим вариантом, так как именно это SqlHelper делает внутренне, когда передает строку соединения (в отличие от объектаSqlConnection
). ПередачаSqlConnection
в метод не возможна, поскольку она будет противоречить разделению DAL / BLL, которое я пытаюсь сохранить. ПодходAction<SqlDataReader>
я не знаком, но кажется немного трудным. Просто чтобы убедиться - можете ли вы указать мне хорошее место, чтобы на самом деле увидеть, что именно это означает?