当这些依赖项需要运行时值时如何注入依赖项?
How to inject dependencies when those dependencies need a runtime value?
我正在实施一个 ASP.NET MVC 应用程序并且需要实施 存储库工作单元 模式。我的实现是这样设计的:
UnitOfWork
对象负责根据需要发出 COMMIT
和 ROLLBACK
。
UnitOfWork
对象包含从内部数据库连接获得的 Transaction
属性。该对象为 UnitOfWork
. 中的操作提供原子性
UnitOfWork
对象包含存储库作为在运行时注入的属性。
- 每个存储库都需要提供
UnitOfWork
创建的 IDbTransaction
对象以支持原子性。
所以现在我发现自己处于一种奇怪的情况,在 UnitOfWork
中,必须注入需要 UnitOfWork
本身的 属性 的存储库才能被实例化。所以我的问题是:我该怎么做?或者设计中的某些内容必须更改?
我目前正在使用 SQL 服务器,我使用 Dapper 进行 SQL 调用。另外,我正在考虑使用 Autofac 作为 DI 框架。
到目前为止,我所做的是实施 UOW 和示例存储库。代码如下
IRepository.cs
:
public interface IRepository<TObj, TKey>
{
Task<TObj> DetallesAsync(TKey id);
Task<TKey> AgregarAsync(TObj obj);
}
DbRepository.cs
:
public abstract class DbRepository
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
protected IDbConnection Connection
{
get => _connection;
}
protected IDbTransaction Transaction
{
get => _transaction;
}
public DbRepository(IDbTransaction transaction)
{
_transaction = transaction;
_connection = _transaction.Connection;
}
}
RolRepository.cs
:
public class MSSQLRolRepository : DbRepository, IRolRepository
{
public MSSQLRolRepository(IDbTransaction transaction)
: base(transaction)
{
}
public async Task<int> AgregarAsync(Rol obj)
{
var result = await Connection.ExecuteScalarAsync<int>(MSSQLQueries.RolAgregar, param: obj, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesAsync(int id)
{
var param = new { Id = id };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetalles, param: param, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesPorNombreAsync(string nombre)
{
var param = new { Nombre = nombre };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetallesPorNombre, param: param, transaction: Transaction);
return result;
}
public async Task<Rol[]> ListarAsync(int pagina, int itemsPorPagina)
{
var param = new { Pagina = pagina, ItemsPorPagina = itemsPorPagina };
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, param: param, transaction: Transaction);
return result.ToArray();
}
public async Task<Rol[]> ListarTodosAsync()
{
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, transaction: Transaction);
return result.ToArray();
}
}
IUnitOfWork.cs
:
public interface IUnitOfWork : IDisposable
{
IDbTransaction Transaction { get; }
IDenunciaRepository DenunciaRepository { get; }
IUsuarioRepository UsuarioRepository { get; }
IRolRepository RolRepository { get; }
void Commit();
void Rollback();
}
MSSQLUnitOfWork.cs
:
public class MSSQLUnitOfWork : IUnitOfWork
{
private bool _already_disposed = false;
private IDbConnection _connection;
private IDbTransaction _transaction;
private IDenunciaRepository _denuncia_repository;
private IUsuarioRepository _usuario_repository;
private IRolRepository _rol_repository;
public IDbTransaction Transaction
{
get => _transaction;
}
public IDenunciaRepository DenunciaRepository
{
get => _denuncia_repository;
}
public IUsuarioRepository UsuarioRepository
{
get => _usuario_repository;
}
public IRolRepository RolRepository
{
get => _rol_repository;
}
public MSSQLUnitOfWork()
{
var connection_string = ConfigurationManager.ConnectionStrings["MSSQL"].ConnectionString;
_connection = new SqlConnection(connection_string);
_connection.Open();
_transaction = _connection.BeginTransaction();
//TODO: Crear repos con transacción
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
protected virtual void Dispose(bool disposeManagedObjects)
{
if (!_already_disposed)
{
if (disposeManagedObjects)
{
_transaction?.Dispose();
_connection?.Dispose();
}
_already_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
我向您推荐 3 种不同的东西。
在您实例化 UnitOfWork 的存储库中开始、提交和回滚您的数据事务——我至少推荐
创建服务class,您可以在其中创建 UnitOfWork 的实例并将其实例或 DBContext 传递给事务中涉及的存储库
在 UnitOfWork 中创建存储库实例 class 知道当前的 DBContext 然后您可以从 UnitOfWork 访问存储库操作并在同一上下文中启动和结束事务。更多推荐
类似于:
UnitOfWorkInstance.MyRepositoryA.AddAsync(...);
UnitOfWorkInstance.MyRepositoryB.AddAsync(...);
UnitOfWorkInstance.Commit();
I find myself in the strange situation of, in UnitOfWork, having to inject repositories that need a property of UnitOfWork itself in order to be instantiated.
看起来你有一个循环依赖,循环依赖总是一个糟糕的设计,你应该打破它。如果您关注 Single Responsibility Principle,则不会发生这种情况。如果发生这种情况,服务可能有太多的责任,你应该在多个服务中打破它,或者有时是因为你的服务太少,这些服务应该重新组合。
在你的情况下,IUnitOfWork
似乎负有太多责任。这项服务的目标是什么?它的职责是什么?
对我来说,这个服务不应该有任何存储库,这个服务不需要任何。如果任何其他服务需要这样的存储库,他们只需添加对它的依赖。此外,存储库不需要依赖 IUnitOfWork
,而只依赖 IDbTransaction
。此外 IDbTransaction
和 IDbConnection
应该在你的依赖注入器中配置。如果出于任何原因需要 IUnitOfWork
实例化这些服务,您可以执行类似
的操作
builder.RegisterType<MSSQLUnitOfWork>()
.As<IUnitOfWork>()
.InstancePerLifetimeScope()
builder.Register(c => c.Resolve<IUnitOfWork>().Transation)
.As<IDbTransaction>();
builder.Register(c => c.Resolve<IUnitOfWork>().Connection)
.As<IDbConnection>();
我正在实施一个 ASP.NET MVC 应用程序并且需要实施 存储库工作单元 模式。我的实现是这样设计的:
UnitOfWork
对象负责根据需要发出COMMIT
和ROLLBACK
。UnitOfWork
对象包含从内部数据库连接获得的Transaction
属性。该对象为UnitOfWork
. 中的操作提供原子性
UnitOfWork
对象包含存储库作为在运行时注入的属性。- 每个存储库都需要提供
UnitOfWork
创建的IDbTransaction
对象以支持原子性。
所以现在我发现自己处于一种奇怪的情况,在 UnitOfWork
中,必须注入需要 UnitOfWork
本身的 属性 的存储库才能被实例化。所以我的问题是:我该怎么做?或者设计中的某些内容必须更改?
我目前正在使用 SQL 服务器,我使用 Dapper 进行 SQL 调用。另外,我正在考虑使用 Autofac 作为 DI 框架。
到目前为止,我所做的是实施 UOW 和示例存储库。代码如下
IRepository.cs
:
public interface IRepository<TObj, TKey>
{
Task<TObj> DetallesAsync(TKey id);
Task<TKey> AgregarAsync(TObj obj);
}
DbRepository.cs
:
public abstract class DbRepository
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
protected IDbConnection Connection
{
get => _connection;
}
protected IDbTransaction Transaction
{
get => _transaction;
}
public DbRepository(IDbTransaction transaction)
{
_transaction = transaction;
_connection = _transaction.Connection;
}
}
RolRepository.cs
:
public class MSSQLRolRepository : DbRepository, IRolRepository
{
public MSSQLRolRepository(IDbTransaction transaction)
: base(transaction)
{
}
public async Task<int> AgregarAsync(Rol obj)
{
var result = await Connection.ExecuteScalarAsync<int>(MSSQLQueries.RolAgregar, param: obj, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesAsync(int id)
{
var param = new { Id = id };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetalles, param: param, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesPorNombreAsync(string nombre)
{
var param = new { Nombre = nombre };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetallesPorNombre, param: param, transaction: Transaction);
return result;
}
public async Task<Rol[]> ListarAsync(int pagina, int itemsPorPagina)
{
var param = new { Pagina = pagina, ItemsPorPagina = itemsPorPagina };
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, param: param, transaction: Transaction);
return result.ToArray();
}
public async Task<Rol[]> ListarTodosAsync()
{
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, transaction: Transaction);
return result.ToArray();
}
}
IUnitOfWork.cs
:
public interface IUnitOfWork : IDisposable
{
IDbTransaction Transaction { get; }
IDenunciaRepository DenunciaRepository { get; }
IUsuarioRepository UsuarioRepository { get; }
IRolRepository RolRepository { get; }
void Commit();
void Rollback();
}
MSSQLUnitOfWork.cs
:
public class MSSQLUnitOfWork : IUnitOfWork
{
private bool _already_disposed = false;
private IDbConnection _connection;
private IDbTransaction _transaction;
private IDenunciaRepository _denuncia_repository;
private IUsuarioRepository _usuario_repository;
private IRolRepository _rol_repository;
public IDbTransaction Transaction
{
get => _transaction;
}
public IDenunciaRepository DenunciaRepository
{
get => _denuncia_repository;
}
public IUsuarioRepository UsuarioRepository
{
get => _usuario_repository;
}
public IRolRepository RolRepository
{
get => _rol_repository;
}
public MSSQLUnitOfWork()
{
var connection_string = ConfigurationManager.ConnectionStrings["MSSQL"].ConnectionString;
_connection = new SqlConnection(connection_string);
_connection.Open();
_transaction = _connection.BeginTransaction();
//TODO: Crear repos con transacción
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
protected virtual void Dispose(bool disposeManagedObjects)
{
if (!_already_disposed)
{
if (disposeManagedObjects)
{
_transaction?.Dispose();
_connection?.Dispose();
}
_already_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
我向您推荐 3 种不同的东西。
在您实例化 UnitOfWork 的存储库中开始、提交和回滚您的数据事务——我至少推荐
创建服务class,您可以在其中创建 UnitOfWork 的实例并将其实例或 DBContext 传递给事务中涉及的存储库
在 UnitOfWork 中创建存储库实例 class 知道当前的 DBContext 然后您可以从 UnitOfWork 访问存储库操作并在同一上下文中启动和结束事务。更多推荐
类似于:
UnitOfWorkInstance.MyRepositoryA.AddAsync(...);
UnitOfWorkInstance.MyRepositoryB.AddAsync(...);
UnitOfWorkInstance.Commit();
I find myself in the strange situation of, in UnitOfWork, having to inject repositories that need a property of UnitOfWork itself in order to be instantiated.
看起来你有一个循环依赖,循环依赖总是一个糟糕的设计,你应该打破它。如果您关注 Single Responsibility Principle,则不会发生这种情况。如果发生这种情况,服务可能有太多的责任,你应该在多个服务中打破它,或者有时是因为你的服务太少,这些服务应该重新组合。
在你的情况下,IUnitOfWork
似乎负有太多责任。这项服务的目标是什么?它的职责是什么?
对我来说,这个服务不应该有任何存储库,这个服务不需要任何。如果任何其他服务需要这样的存储库,他们只需添加对它的依赖。此外,存储库不需要依赖 IUnitOfWork
,而只依赖 IDbTransaction
。此外 IDbTransaction
和 IDbConnection
应该在你的依赖注入器中配置。如果出于任何原因需要 IUnitOfWork
实例化这些服务,您可以执行类似
builder.RegisterType<MSSQLUnitOfWork>()
.As<IUnitOfWork>()
.InstancePerLifetimeScope()
builder.Register(c => c.Resolve<IUnitOfWork>().Transation)
.As<IDbTransaction>();
builder.Register(c => c.Resolve<IUnitOfWork>().Connection)
.As<IDbConnection>();