2017-02-16 53 views
1

我們正在使用dapper ORM通過調用存儲過程來訪問數據。當前代碼有一個BusinessFunctions類,該類繼承另一個具有輔助方法來執行存儲過程的DataFunctions類。在ASP.NET Core中使用存儲庫模式中的接口和抽象類

我對此代碼不滿意。這太僵硬了,沒有前途證明。最重要的是,它不是編碼到接口而是編碼到實現。我提出了一個接口IRepository和一個抽象類Repository,它實現了所有的Helper泛型方法。然後創建實現抽象Repository類的BusinessRepository並調用泛型助手方法。同樣,我的同事告訴我要刪除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); 


    } 
} 

回答

3

我認爲你對代碼不滿意的原因是它似乎將服務功能混合到存儲庫層。存儲庫層應該簡單地調用存儲過程。

public async Task<bool> IsHostedTokenizeCardAllowedAsync(string businessGuid) 
{ 
    var b = await FindAsync(businessGuid); 
    if (b.HostedPaymentEnabled) 
     return true; 
    else 
     return false; 
} 

例如,這是一個很好的候選人在服務層。

您的回購層應該只有您的ConnectionManager或通過IoC注入的連接工廠。

我們使用的技巧是在數據模型字段上放置一個屬性,我們知道它將存儲過程參數(通常是大部分或全部)。然後我們有一個擴展方法,它反映了這些屬性,並提取了創建精細的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); 
    } 
} 

你甚至可以裝飾在國際奧委會,這些策略的方法來隱藏/根據需要增加它們。 (在結構地圖中是.DecorateAllWith()

簡而言之,將任何邏輯移動到服務層,考慮創建DynamicParameters列表的通用擴展方法,並通過IoC注入連接工廠。我想你會發現分離的擔憂會使事情顯着地變得簡單化