簡體   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