2017-03-22 55 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的新实例)中,而不做任何更改。 –