C# 分离NpgsqlConnection的关注点
我有一个体系结构,其中多个存储库类实现了针对数据库运行的不同查询和命令。我想将“连接到数据库”+“运行查询”和“提供查询以运行”+“处理结果”这两个问题分开。我编写了一个连接类,然后将其作为构造函数参数传递给存储库,如下所示:C# 分离NpgsqlConnection的关注点,c#,npgsql,C#,Npgsql,我有一个体系结构,其中多个存储库类实现了针对数据库运行的不同查询和命令。我想将“连接到数据库”+“运行查询”和“提供查询以运行”+“处理结果”这两个问题分开。我编写了一个连接类,然后将其作为构造函数参数传递给存储库,如下所示: public class PostgreSqlConnection { private string connectionString; public PostgreSqlConnection(string connectionString) {
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();
}
}
撇开代码重复不谈,这不起作用,因为在查询案例中,连接将在ExecuteQueryCommand
方法的末尾被处理,读取器将停止工作
使用语句删除可以解决这个问题,但从我收集的信息来看,这不是一个好的做法。编写一个Dispose
/Disconnect
方法,我可以在存储库中调用该方法,这也会起作用,但处理连接不是存储库的工作
我如何将关注点分开并正确地处理项目?我认为这里可以帮助的是工作单元模式。基本上,使用UnitOfWork
类,您可以处理连接、存储库实例和事务(如果您要将数据保存到数据库中)。您还可以灵活地打开连接/事务,然后跨多个存储库执行多个命令,最后您可以提交或回滚事务,或者在纯读取的情况下,您只需使用IDisposable
模式关闭连接
UnitOfWork
类将处理连接部分,您的BaseRepository
(抽象类)将有您的通用执行方法,它将处理查询/命令的执行。具体的存储库(在您的例子中是A和B)将继承自BaseRepository
,只需准备命令/查询并从BaseRepository
调用Execute方法即可。他们的职责基本上是准备查询/命令并处理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
,它将保存查询/命令执行的“通用”方法:
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();
}
}
你的回答帮助我解决了这个问题,但我的回答与实际模式有点不同。这只是一个细节,但我认为您的代码不会在NpgsqlCommand
上调用Dispose
(如果您处理连接,我甚至不确定是否需要Dispose?)。我应该发布我的解决方案吗?我不经常使用stackoverflow我上面给你的代码只是给你方向,你可以根据需要编辑或改进它。但是,我已经更新了UnitOfWork
代码,以使一次性模式更好,而且NpsqlCommand
现在正在使用using
,因此当回购方法返回时将对其进行处理。这样,它被明确地处理,你是安全的。
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();
}
}
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
}
}
}
using(var uow = new UnitOfWork("place_your_conn_string", withTransaction: true))
{
var countB = await uow.PostgreSqlRepositoryB.GetCountB();
uow.PostgreSqlRepositoryB.SaveSomethingToA("123456");
uow.Commit();
}