[英]Using interface and abstract class in repository pattern in ASP.NET Core
我们目前正在使用dapper ORM通过调用存储过程来访问数据。 当前代码有一个类BusinessFunctions,它继承了另一个类DataFunctions,它们有辅助方法来执行存储过程。
我对这段代码不满意。 它过于僵化而不是未来的证据。 最重要的是,它没有编码到接口而是编码为实现。 我提出了一个带有抽象类Repository的接口IRepository,它实现了所有helper泛型方法。 然后我创建实现抽象Repository类的BusinessRepository并调用泛型helpers方法。 同样,我的同事告诉我删除IRepository接口并只使用Repository抽象类。
public class BusinessFunctions : DataFunctions
{
public BusinessFunctions(ConnectionManager conMgr, LogWriter logWriter, AppUser appUser) : base(conMgr, logWriter, appUser)
{
}
public async Task<Business> FindAsync(int businessId)
{
throw new NotImplementedException();
}
public async Task<Business> FindAsync(string businessGuid)
{
var lst = await StoredProcQueryAsync<Business>("spBusinessGetSetupGUID", new { BusinessGuid = businessGuid });
if (lst.Count() == 0)
throw new NotFoundInDatabaseException("Business", businessGuid);
else
return lst.Single();
}
public async Task<bool> IsHostedTokenizeCardAllowedAsync(string businessGuid)
{
var b = await FindAsync(businessGuid);
if (b.HostedPaymentEnabled)
return true;
else
return false;
}
}
public class DataFunctions : IDisposable
{
private ConnectionManager _conMgr;
private LogWriter _logWriter;
private AppUser _appUser;
public ConnectionManager ConnMgr
{
get { return _conMgr; }
}
protected LogWriter Logger
{
get { return _logWriter; }
}
protected AppUser User
{
get { return _appUser; }
}
public DataFunctions(ConnectionManager conMgr, LogWriter logWriter, AppUser appUser)
{
_conMgr = conMgr;
_logWriter = logWriter;
_appUser = appUser;
}
public void Dispose()
{
}
public async Task StoredProcExecuteNonQueryAsync(string storedProc,
List<StoredProcParameter> storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default,
SqlAccessType accessType = SqlAccessType.ReadWrite
)
{
using (SqlConnection conn = new SqlConnection(ConnMgr.SqlConnectionString))
{
await conn.OpenAsync();
await StoredProcExecuteNonQueryAsync(conn,
storedProc,
storedProcParameters: storedProcParameters,
commandTimeout: commandTimeout,
accessType: accessType);
}
}
public async Task StoredProcExecuteNonQueryAsync(SqlConnection conn,
string storedProc,
List<StoredProcParameter> storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default,
SqlAccessType accessType = SqlAccessType.ReadWrite,
SqlTransaction trans = null
)
{
using (SqlCommand cmd = new SqlCommand(storedProc, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = (int)commandTimeout;
if (trans != null) cmd.Transaction = trans;
if (storedProcParameters != null)
{
foreach(var p in storedProcParameters)
{
cmd.Parameters.Add(p.ToSqlParameter());
}
}
await cmd.ExecuteNonQueryAsync();
}
}
public async Task<IEnumerable<T>> StoredProcQueryAsync<T>(string storedProc,
object storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default,
SqlAccessType accessType = SqlAccessType.ReadWrite)
{
using (SqlConnection conn = new SqlConnection(ConnMgr.SqlConnectionString))
{
conn.Open();
return await StoredProcQueryAsync<T>(conn,
storedProc,
storedProcParameters,
commandTimeout);
}
}
public async Task<IEnumerable<T>> StoredProcQueryAsync<T>(SqlConnection conn,
string storedProc,
object storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default)
{
return await conn.QueryAsync<T>(storedProc,
commandType: CommandType.StoredProcedure,
commandTimeout: (int)commandTimeout,
param: storedProcParameters);
}
}
我认为您对代码不满意的原因是它似乎将服务功能混合到存储库层。 存储库层应该只调用存储过程。
public async Task<bool> IsHostedTokenizeCardAllowedAsync(string businessGuid)
{
var b = await FindAsync(businessGuid);
if (b.HostedPaymentEnabled)
return true;
else
return false;
}
例如,这是服务层中的一个很好的候选者。
您的repo层应该只通过IoC注入ConnectionManager或Connection工厂。
我们使用的技巧是在数据模型字段上放置一个属性,我们知道它们将是存储过程参数(通常是大多数或全部)。 然后我们有一个扩展方法,它反映了属性并提取了创建一个精巧的DynamicParameters对象的字段,值和类型。 我们的大多数存储库调用如下所示:
public async Task<User> AddUserAsync(UserAdd user)
{
using (var connection = _connectionFactory.Create()
{
var result = connection.ExecuteAsync("dbo.AddUser", user.GetParameters(), commandType: CommandType.StoredProcedure";
return result;
}
}
它相对快速且易于使用。 获取非常容易测试。 插入/删除/更新不是那么多。 您需要模拟SqlConnection,这可能会有问题。
此外,如果您进入可能发生变化的更复杂区域,您可以使用策略模式将方法移动到自己的类中。 下面是将add方法拆分为自己的类的示例:
public class MyRepository
{
private readonly IAddMethod<UserAdd> _addMethod;
private readonly IConnectionFactory _connectionFactory;
public MyRepository(IAddMethod<UserAdd> userAddMethod,
IConnectionFactory connectionFactory)
{
//..guard clauses, assignments, etc.
}
public async Task<int> AddAsync(UserAdd user)
{
return _addMethod.AddAsync(user);
}
}
您甚至可以在IoC中修饰这些策略方法,以根据需要隐藏/扩充它们。 (在结构图中,它是.DecorateAllWith()
简而言之,将任何逻辑移动到服务层,考虑用于创建DynamicParameters列表的通用扩展方法,并通过IoC注入连接工厂。 我想你会发现关注点的分离会大大简化事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.