Короткий синтаксис ADO .NET (часть III)
Короткий синтаксис ADO .NET (часть IV)
В предыдущей части я привел перечень действий, необходимых для выполнения запроса и корректного овсобождения ресурсов. Начнем с открытия и закрытия соединения. В пределе хочется иметь сущность, которая бы:
- анализировала состояние указанного соединения, и открывала бы его, если оно не открыто;
- гарантированно закрывала бы соединение после использования, в случае если соединение было первоначально закрыто;
- обращение к такой конструкции не занимало бы много места.
public class ConnectionSession : IDisposable
{
public ConnectionSession(IDbConnection dbConnection)
{
if (dbConnection == null)
{
throw new ArgumentNullException("dbConnection");
}
DbConnection = dbConnection;
WasOpened = ConnectionState.Open == (DbConnection.State & ConnectionState.Open);
if (!WasOpened)
{
DbConnection.Open();
}
}
public IDbConnection DbConnection { get; private set; }
public bool WasOpened { get; private set; }
public void Dispose()
{
if (!WasOpened)
{
DbConnection.Close();
}
}
}Прежде чем начну комментировать код, хочу заметить, что в связи с грядущим переходом на Как я уже сказал, интерфейс IDisposable реализован чисто для поддержания короткого синтаксиса using. Сей объект не нуждается в финализации и DbConnection сам будет освобождать необходимые ресурсы при сборке мусора. Быть может когда-нибудь я уделю связке using/IDisposable больше внимания.
Почему я использовал IDbConnection интерфейс, вместо базового класса DbConnection? Очень просто. При тестировании я могу подменить объект без средств тестирования, использующих инструментацию сборок. Для генерации mock объектов и заглушек (stub) я использую
В постах я буду приводить некоторые тесты, но далеко не все, которые пишу.
[TestMethod]
public void UsingConstructionWithOpenedConnectionTest()
{
var connection = MockRepository.GenerateStub<IDbConnection>();
connection.Stub(x => x.State).Return(ConnectionState.Closed);
using (new ConnectionSession(connection))
{
connection.AssertWasCalled(x => x.Open());
connection.AssertWasNotCalled(x => x.Close());
}
connection.AssertWasCalled(x => x.Close());
}
[TestMethod]
public void UsingConstructionWithClosedConnectionTest()
{
var connection = MockRepository.GenerateStub<IDbConnection>();
connection.Stub(x => x.State).Return(ConnectionState.Open);
using (new ConnectionSession(connection))
{
connection.AssertWasNotCalled(x => x.Open());
}
connection.AssertWasNotCalled(x => x.Close());
}Несмотря на то, что в каждом тесте рекомендуется проверять не более одного условия, я напичкал их множеством условий, потому как я тестировал поведение ConnectionSession в рамках конструкции using. Это уже даже не unit тест, а некий интеграционный. Unit тесты класса ConnectionSession я опустил. Их больше, чем хотелось бы вставлять в пост. Оговорки в условиях к ConnectionSession (вначале поста) нужны для безопасного использования соединения в области действия нескольких вложенных блоков using для ConnectionSession объектов. В рамках C# 3.0 будет удобен следующй extension метод:public static class ConnectionSessionExtensions
{
public static ConnectionSession CreateSession(this IDbConnection connection)
{
return new ConnectionSession(connection);
}
}это позволит писать код в стилеusing (connection.CreateSession())
{
return command.ExecuteNonQuery();
}Довольно емкий смысл вложен в одну строчку:если соединение не открыто, то открыть его, если было закрыто, то закрыть после выхода из блока.Уже этот код позволит многим сделать обращения к ADO (и не только) более лаконичными. Но на этом не остановимся, продолжение следует.
Не совсем понятно, почему в методе Dispose класса ConnectionSession стоит проверка
ОтветитьУдалитьif (!WasOpened) {...}
Может быть, должно быть
if (WasOpened) {...} ?
WasOpened хранит признак того, было ли открыто соединение до создания экземпляра ConnectionSession. И если оно было открыто, то за закрытие соединения должен отвечать тот, кто его открыл. А если оно не было открыто и его открыл именно этот экземпляр сессии, то в этом случае следует закрыть соединение.
ОтветитьУдалитьТакая проверка позволяет использовать несколько вложенных сессий с одним соединением.