繁体   English   中英

在ASP.NET Core中使用存储库模式中的接口和抽象类

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM