2017-03-22 50 views
5

這可能更多的代碼審查問題,而不是堆棧溢出。存儲庫設計模式與Dapper

我正在使用DAPPER進行MicroORM檢索並將數據保存到SQL Server 2014.我已經在DTO Proj中獲得了代表從數據庫中檢索到的數據或保存到數據庫的DTO類。

我使用存儲庫模式,以便在我的業務層是否需要一個倉庫,我使用構造DI來注入依賴,然後調用的庫的方法做的工作。

所以說我有2個服務叫做CustomerService和CarService。

然後我有2個存儲庫一個CustomerRepository和一個CarRepository。

我有限定在每個存儲庫中的所有方法,然後將具體實現的接口。

的示例性方法如下所示(調用一個存儲過程做DB INSERT(注意,存儲的過程的實際字符串變量在類的頂部被定義爲私有字符串):

public void SaveCustomer(CustomerDTO custDTO) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      db.Execute(saveCustSp, custDTO, commandType: CommandType.StoredProcedure); 
     } 
    } 

這一切工作正常,但我發現自己在每一個存儲庫中每個方法重複使用的塊。我有下面列出兩個真正的問題。

有沒有更好的辦法,我可以使用或許在某種程度上使用BaseRepository類每個其他Repository從中繼承,Base將實現數據庫連接的實例化?

對於系統上的多個併發用戶,這仍然可以正常工作嗎?

**** ****更新

基於西拉答案我已經創建瞭如下

public interface IBaseRepository 
{ 
    void Execute(Action<IDbConnection> query); 
} 

public class BaseRepository: IBaseRepository 
{ 
     public void Execute(Action<IDbConnection> query) 
     { 
      using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
      { 
       query.Invoke(db); 
      } 
     } 
} 

然而,在我的倉庫,我還有其他的方法,如下面的:

public bool IsOnlyCarInStock(int carId, int year) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      var car = db.ExecuteScalar<int>(anotherStoredSp, new { CarID = carId, Year = year }, 
           commandType: CommandType.StoredProcedure); 

      return car > 0 ? true : false; 
     } 
    } 

public IEnumerable<EmployeeDTO> GetEmployeeDetails(int employeeId) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      return db.Query<EmployeeDTO>(anotherSp, new { EmployeeID = employeeId }, 
           commandType: CommandType.StoredProcedure); 
     } 
    } 

什麼是使用通用類型T將這些添加到我的基本存儲庫的正確方法,所以我可以返回任何類型的DTO或任何C#本機類型

+0

這是爲了實現它,你需要讓你的BaseRepository一次性處置您的IDbConnection的方式。您可以在Microsoft文檔中查看有關使用存儲庫模式和工作單元模式的信息https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef- 5-using-mvc-4 /實現存儲庫和工作單元模式-asp-net-mvc-application – OrcusZ

+0

「使用」塊是一個必要的邪惡,因爲你打開連接到需要關閉的數據庫。所以重複是必要的。我只會建議不要陷入整個資源庫設計模式的東西.... –

+0

@Callum - 你會提出什麼樣的模式,或者你可以用一個例子來說明。我曾看過使用CQRS,但我覺得庫如上所述基於KISS –

回答

7

當然,創建和處置連接的函數將很有用。

protected void Execute(Action<IDbConnection> query) 
{ 
    using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
    { 
     query.Invoke(db); 
    } 
} 

而且您簡化呼叫網站:

public void SaveCustomer(CustomerDTO custDTO) 
{ 
    Execute(db => db.Execute(saveCustSp, custDTO, CommandType.StoredProcedure)); 
} 

隨着返回值:

public T Get<T>(Func<IDbConnection, T> query) 
{ 
    using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
    { 
     return query.Invoke(db); 
    } 
} 

在您的通話網站,只寫你希望使用的邏輯。

public IEnumerable<EmployeeDTO> GetEmployeeDetails(int employeeId) 
{ 
    return Get<IEnumerable<EmployeeDTO>(db => 
     db.Query<EmployeeDTO>(anotherSp, new { EmployeeID = employeeId }, CommandType.StoredProcedure)); 
} 
+0

謝謝Silas。我看看那個。你會推薦Puttinh Execute方法在一個基本的存儲庫和任何其他存儲庫繼承?也將query.Invoke工作的任何類型的Dapper db.function? –

+1

如果您期望有許多需要此功能的類,那麼基礎知識庫是一個好主意。 –

+1

調用查詢/操作適用於任何想要對IDbConnection執行的操作,包括Dapper方法和非Dapper方法。 –

3

這不是你的問題直接相關。但我建議你考慮使用DapperExtensions。

起初,我沒有用小巧玲瓏來實現存儲庫模式。缺點是我必須全部寫下查詢;這是非常粘稠的。由於硬編碼查詢,幾乎不可能編寫通用存儲庫。

最近,我升級我的代碼使用DapperExtensions。這解決了很多問題。

以下是通用的存儲庫:

public abstract class BaseRepository<T> where T : BasePoco 
{ 
    internal BaseRepository(IUnitOfWork unitOfWork) 
    { 
     dapperExtensionsProxy = new DapperExtensionsProxy(unitOfWork); 
    } 

    DapperExtensionsProxy dapperExtensionsProxy = null; 

    protected bool Exists() 
    { 
     return (GetCount() == 0) ? false : true; 
    } 

    protected int GetCount() 
    { 
     var result = dapperExtensionsProxy.Count<T>(null); 
     return result; 
    } 

    protected T GetById(Guid id) 
    { 
     var result = dapperExtensionsProxy.Get<T>(id); 
     return result; 
    } 
    protected T GetById(string id) 
    { 
     var result = dapperExtensionsProxy.Get<T>(id); 
     return result; 
    } 

    protected List<T> GetList() 
    { 
     var result = dapperExtensionsProxy.GetList<T>(null); 
     return result.ToList(); 
    } 

    protected void Insert(T poco) 
    { 
     var result = dapperExtensionsProxy.Insert(poco); 
    } 

    protected void Update(T poco) 
    { 
     var result = dapperExtensionsProxy.Update(poco); 
    } 

    protected void Delete(T poco) 
    { 
     var result = dapperExtensionsProxy.Delete(poco); 
    } 

    protected void DeleteById(Guid id) 
    { 
     T poco = (T)Activator.CreateInstance(typeof(T)); 
     poco.SetDbId(id); 
     var result = dapperExtensionsProxy.Delete(poco); 
    } 
    protected void DeleteById(string id) 
    { 
     T poco = (T)Activator.CreateInstance(typeof(T)); 
     poco.SetDbId(id); 
     var result = dapperExtensionsProxy.Delete(poco); 
    } 

    protected void DeleteAll() 
    { 
     var predicateGroup = new PredicateGroup { Operator = GroupOperator.And, Predicates = new List<IPredicate>() }; 
     var result = dapperExtensionsProxy.Delete<T>(predicateGroup);//Send empty predicateGroup to delete all records. 
    } 

正如你可以看到在上面的代碼中,大多數方法都只是包裝器在底層DapperExtensionsProxy類。 DapperExtensionsProxy內部也管理您可以在下面看到的UnitOfWork。 這兩個類可以結合使用,沒有任何問題。我個人更喜歡將它們分開。

您還可以注意到,其他方法ExistsDeleteByIdDeleteAll被實現,但它們不是DapperExtensionsProxy的一部分。

方法poco.SetDbId在每個POCO類來設置它的標識符屬性定義。就我而言,POCO的標識符可能有不同的數據類型和名稱。

以下是DapperExtensionsProxy

internal sealed class DapperExtensionsProxy 
{ 
    internal DapperExtensionsProxy(IUnitOfWork unitOfWork) 
    { 
     this.unitOfWork = unitOfWork; 
    } 

    IUnitOfWork unitOfWork = null; 

    internal int Count<T>(object predicate) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Count<T>(predicate, unitOfWork.Transaction); 
     return result; 
    } 

    internal T Get<T>(object id) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Get<T>(id, unitOfWork.Transaction); 
     return result; 
    } 

    internal IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort = null, bool buffered = false) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.GetList<T>(predicate, sort, unitOfWork.Transaction, null, buffered); 
     return result; 
    } 

    internal IEnumerable<T> GetPage<T>(object predicate, int page, int resultsPerPage, IList<ISort> sort = null, bool buffered = false) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.GetPage<T>(predicate, sort, page, resultsPerPage, unitOfWork.Transaction, null, buffered); 
     return result; 
    } 

    internal dynamic Insert<T>(T poco) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Insert<T>(poco, unitOfWork.Transaction); 
     return result; 
    } 

    internal void Insert<T>(IEnumerable<T> listPoco) where T : BasePoco 
    { 
     unitOfWork.Connection.Insert<T>(listPoco, unitOfWork.Transaction); 
    } 

    internal bool Update<T>(T poco) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Update<T>(poco, unitOfWork.Transaction); 
     return result; 
    } 

    internal bool Delete<T>(T poco) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Delete<T>(poco, unitOfWork.Transaction); 
     return result; 
    } 

    internal bool Delete<T>(object predicate) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Delete<T>(predicate, unitOfWork.Transaction); 
     return result; 
    } 
} 

這也採用的UnitOfWork這是解釋here

+0

不錯@Amit Joshi ...如果我有多個數據庫連接字符串,它會工作嗎?是的,建議wat最小的更改需要完成嗎?請建議 – Ljt

+1

@LajithKumar:無需更改就可以使用多個數據庫實例工作。請注意,UnitOfWork就像這個'BaseRepository(IUnitOfWork unitOfWork)'一樣被注入到Repository中。您可以爲每個數據庫實例創建新的UnitOfWork,並將其注入同一個類(Repository的新實例)中,而不做任何更改。 –