分离 NpgsqlConnection 的关注点
Separating concerns for NpgsqlConnection
我有一个架构,其中多个存储库 classes 实现针对数据库的 运行 不同查询和命令。我想将“连接到数据库”+“运行宁查询”和“向运行提供查询”+“处理结果”的关注点分开。我写了一个 Connection class 然后作为构造函数参数传递给存储库,如下所示:
public class PostgreSqlConnection
{
private string connectionString;
public PostgreSqlConnection(string connectionString)
{
this.connectionString = connectionString;
}
public async Task<NpgsqlDataReader> ExecuteQueryCommand(NpgsqlCommand command)
{
using NpgsqlConnection connection = new NpgsqlConnection(this.connectionString);
await connection.OpenAsync();
command.Connection = connection;
command.Prepare();
return await command.ExecuteReaderAsync();
}
public async Task ExecuteNonQueryCommand(NpgsqlCommand command)
{
using NpgsqlConnection connection = new NpgsqlConnection(this.connectionString);
await connection.OpenAsync();
command.Connection = connection;
command.Prepare();
await command.ExecuteNonQueryAsync();
}
}
实例化看起来像这样:
PostgreSqlConnection connection = new PostgreSqlConnection("...connection string");
IRepositoryA repA = new PostgreSqlRepositoryA(connection);
IRepositoryB repB = new PostgreSqlRepositoryB(connection);
抛开代码重复,这是行不通的,因为在查询情况下,连接将在 ExecuteQueryCommand
方法结束时被处理掉,reader 将停止工作。
删除 using
语句可以解决此问题,但据我所知,这不是好的做法。编写一个我可以在存储库中调用的 Dispose
/ Disconnect
方法也是可行的,但处理连接不是存储库的工作。
我怎样才能将问题分开并妥善处理物品?
我认为这里可以提供帮助的是工作单元模式。基本上,使用 UnitOfWork
class 你可以根据需要处理连接、存储库实例和事务(如果你将数据保存到数据库)。您还可以灵活地打开 connection/transaction,然后跨多个存储库执行多个命令,最后您可以提交或回滚事务,或者在纯读取的情况下,您只需使用 IDisposable
关闭连接模式。
UnitOfWork
class 将处理连接部分,您的 BaseRepository
(抽象 class)将具有您的通用执行方法,它将处理 query/command。具体的存储库(在你的例子中是 A 和 B)将从 BaseRepository
继承,只需准备 commands/queries 并从 BaseRepository
调用 Execute 方法。他们的职责基本上是准备 query/command 并处理 Execute 方法的结果。
请检查代码,因为我没有 Postgres 数据库,无法 100% 测试它。我希望这足以为您提供方向和方法背后的主要思想。
想法是这样的:
在您管理连接、事务(如果需要)和存储库实例的地方实施UnitOfWork
。
public class UnitOfWork : IDisposable
{
private PostgreSqlRepositoryA _postgreSqlRepositoryA;
private PostgreSqlRepositoryB _postgreSqlRepositoryB;
private NpgsqlConnection _sqlConnection;
private NpgsqlTransaction _sqlTransaction;
private bool _disposed;
public UnitOfWork(string connectionString, bool withTransaction)
{
_sqlConnection = new NpgsqlConnection(connectionString);
_sqlConnection.Open();
if (withTransaction)
_sqlTransaction = _sqlConnection.BeginTransaction();
}
public PostgreSqlRepositoryA PostgreSqlRepositoryA
{
get
{
if(_postgreSqlRepositoryA == null)
{
_postgreSqlRepositoryA = new PostgreSqlRepositoryA(_sqlConnection);
}
return _postgreSqlRepositoryA;
}
}
public PostgreSqlRepositoryB PostgreSqlRepositoryB
{
get
{
if (_postgreSqlRepositoryB == null)
{
_postgreSqlRepositoryB = new PostgreSqlRepositoryB(_sqlConnection);
}
return _postgreSqlRepositoryB;
}
}
public void Commit()
{
// hanlde using try-catch
if(_sqlTransaction != null)
{
_sqlTransaction.Commit();
}
}
public void Rollback()
{
if (_sqlTransaction != null)
{
_sqlTransaction.Rollback();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (_sqlTransaction != null)
{
_sqlTransaction.Rollback(); // or throw an Exception for an opened transaction
}
if (_sqlConnection != null)
{
_sqlConnection.Close();
_sqlConnection.Dispose();
}
this._disposed = true;
}
}
}
然后你需要的是一个 BaseRepository
它将保存你的“通用”方法 query/command 执行:
public abstract class BaseRepository
{
protected NpgsqlConnection _sqlConnection;
public BaseRepository(NpgsqlConnection sqlConnection)
{
this._sqlConnection = sqlConnection;
}
public async Task<NpgsqlDataReader> ExecuteQueryCommand(NpgsqlCommand command)
{
command.Connection = _sqlConnection;
command.Prepare();
return await command.ExecuteReaderAsync();
}
public async Task ExecuteNonQueryCommand(NpgsqlCommand command)
{
command.Connection = _sqlConnection;
command.Prepare();
await command.ExecuteNonQueryAsync();
}
}
您的具体存储库实现将如下所示(我没有费心处理 reader,您可以添加该部分):
public class PostgreSqlRepositoryB : BaseRepository
{
public PostgreSqlRepositoryB(NpgsqlConnection sqlConnection)
: base(sqlConnection)
{}
public async Task<int> GetCountB()
{
using (var sqlCommand = new NpgsqlCommand())
{
sqlCommand.CommandText = "select count(1) from TableB";
var reader = await ExecuteQueryCommand(sqlCommand);
// TODO: handle reader
}
}
}
最后你会像这样从你的服务或客户端方法中使用它(如果你只是从数据库读取然后设置 withTransaction
到 false
,你在这种情况下不需要交易):
using(var uow = new UnitOfWork("place_your_conn_string", withTransaction: true))
{
var countB = await uow.PostgreSqlRepositoryB.GetCountB();
uow.PostgreSqlRepositoryB.SaveSomethingToA("123456");
uow.Commit();
}
我有一个架构,其中多个存储库 classes 实现针对数据库的 运行 不同查询和命令。我想将“连接到数据库”+“运行宁查询”和“向运行提供查询”+“处理结果”的关注点分开。我写了一个 Connection class 然后作为构造函数参数传递给存储库,如下所示:
public class PostgreSqlConnection
{
private string connectionString;
public PostgreSqlConnection(string connectionString)
{
this.connectionString = connectionString;
}
public async Task<NpgsqlDataReader> ExecuteQueryCommand(NpgsqlCommand command)
{
using NpgsqlConnection connection = new NpgsqlConnection(this.connectionString);
await connection.OpenAsync();
command.Connection = connection;
command.Prepare();
return await command.ExecuteReaderAsync();
}
public async Task ExecuteNonQueryCommand(NpgsqlCommand command)
{
using NpgsqlConnection connection = new NpgsqlConnection(this.connectionString);
await connection.OpenAsync();
command.Connection = connection;
command.Prepare();
await command.ExecuteNonQueryAsync();
}
}
实例化看起来像这样:
PostgreSqlConnection connection = new PostgreSqlConnection("...connection string");
IRepositoryA repA = new PostgreSqlRepositoryA(connection);
IRepositoryB repB = new PostgreSqlRepositoryB(connection);
抛开代码重复,这是行不通的,因为在查询情况下,连接将在 ExecuteQueryCommand
方法结束时被处理掉,reader 将停止工作。
删除 using
语句可以解决此问题,但据我所知,这不是好的做法。编写一个我可以在存储库中调用的 Dispose
/ Disconnect
方法也是可行的,但处理连接不是存储库的工作。
我怎样才能将问题分开并妥善处理物品?
我认为这里可以提供帮助的是工作单元模式。基本上,使用 UnitOfWork
class 你可以根据需要处理连接、存储库实例和事务(如果你将数据保存到数据库)。您还可以灵活地打开 connection/transaction,然后跨多个存储库执行多个命令,最后您可以提交或回滚事务,或者在纯读取的情况下,您只需使用 IDisposable
关闭连接模式。
UnitOfWork
class 将处理连接部分,您的 BaseRepository
(抽象 class)将具有您的通用执行方法,它将处理 query/command。具体的存储库(在你的例子中是 A 和 B)将从 BaseRepository
继承,只需准备 commands/queries 并从 BaseRepository
调用 Execute 方法。他们的职责基本上是准备 query/command 并处理 Execute 方法的结果。
请检查代码,因为我没有 Postgres 数据库,无法 100% 测试它。我希望这足以为您提供方向和方法背后的主要思想。
想法是这样的:
在您管理连接、事务(如果需要)和存储库实例的地方实施
UnitOfWork
。public class UnitOfWork : IDisposable { private PostgreSqlRepositoryA _postgreSqlRepositoryA; private PostgreSqlRepositoryB _postgreSqlRepositoryB; private NpgsqlConnection _sqlConnection; private NpgsqlTransaction _sqlTransaction; private bool _disposed; public UnitOfWork(string connectionString, bool withTransaction) { _sqlConnection = new NpgsqlConnection(connectionString); _sqlConnection.Open(); if (withTransaction) _sqlTransaction = _sqlConnection.BeginTransaction(); } public PostgreSqlRepositoryA PostgreSqlRepositoryA { get { if(_postgreSqlRepositoryA == null) { _postgreSqlRepositoryA = new PostgreSqlRepositoryA(_sqlConnection); } return _postgreSqlRepositoryA; } } public PostgreSqlRepositoryB PostgreSqlRepositoryB { get { if (_postgreSqlRepositoryB == null) { _postgreSqlRepositoryB = new PostgreSqlRepositoryB(_sqlConnection); } return _postgreSqlRepositoryB; } } public void Commit() { // hanlde using try-catch if(_sqlTransaction != null) { _sqlTransaction.Commit(); } } public void Rollback() { if (_sqlTransaction != null) { _sqlTransaction.Rollback(); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (_sqlTransaction != null) { _sqlTransaction.Rollback(); // or throw an Exception for an opened transaction } if (_sqlConnection != null) { _sqlConnection.Close(); _sqlConnection.Dispose(); } this._disposed = true; } } }
然后你需要的是一个
BaseRepository
它将保存你的“通用”方法 query/command 执行:public abstract class BaseRepository { protected NpgsqlConnection _sqlConnection; public BaseRepository(NpgsqlConnection sqlConnection) { this._sqlConnection = sqlConnection; } public async Task<NpgsqlDataReader> ExecuteQueryCommand(NpgsqlCommand command) { command.Connection = _sqlConnection; command.Prepare(); return await command.ExecuteReaderAsync(); } public async Task ExecuteNonQueryCommand(NpgsqlCommand command) { command.Connection = _sqlConnection; command.Prepare(); await command.ExecuteNonQueryAsync(); } }
您的具体存储库实现将如下所示(我没有费心处理 reader,您可以添加该部分):
public class PostgreSqlRepositoryB : BaseRepository { public PostgreSqlRepositoryB(NpgsqlConnection sqlConnection) : base(sqlConnection) {} public async Task<int> GetCountB() { using (var sqlCommand = new NpgsqlCommand()) { sqlCommand.CommandText = "select count(1) from TableB"; var reader = await ExecuteQueryCommand(sqlCommand); // TODO: handle reader } } }
最后你会像这样从你的服务或客户端方法中使用它(如果你只是从数据库读取然后设置
withTransaction
到false
,你在这种情况下不需要交易):using(var uow = new UnitOfWork("place_your_conn_string", withTransaction: true)) { var countB = await uow.PostgreSqlRepositoryB.GetCountB(); uow.PostgreSqlRepositoryB.SaveSomethingToA("123456"); uow.Commit(); }