使用Dapper的存储库设计模式
这可能是代码审查的问题,而不是堆栈溢出。
我正在使用Dapper for MicroORM来检索并将数据保存到SQL Server 2014.我在DTO Proj中有DTO类,它代表从DB检索到的数据或保存到DB。
我正在使用存储库模式,所以在我的服务层,如果需要存储库,我使用构造函数DI注入该依赖项,然后调用存储库上的方法来完成工作。
所以我要说有2个名为CustomerService和CarService的服务。
然后,我有2个存储库,一个CustomerRepository和一个CarRepository。
我有一个接口,它定义每个存储库中的所有方法,然后定义具体的实现。
下面显示了一个示例方法(调用Stored Proc来执行DB INSERT(注意,存储过程的实际字符串变量被定义为类顶部的私有字符串):
public void SaveCustomer(CustomerDTO custDTO) { using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) { db.Execute(saveCustSp, custDTO, commandType: CommandType.StoredProcedure); } }
这一切都很好但我发现自己在每个存储库中的每个方法中重复使用块。 我有两个真正的问题,如下所述。
是否有一种更好的方法,我可能会以某种方式使用BaseRepository类,每个其他存储库inheritance,Base将实现数据库连接的实例化?
对于系统上的多个并发用户,这仍然可以正常工作吗?
**** ****更新
根据Silas的回答,我创建了以下内容
public interface IBaseRepository { void Execute(Action query); } public class BaseRepository: IBaseRepository { public void Execute(Action 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(anotherStoredSp, new { CarID = carId, Year = year }, commandType: CommandType.StoredProcedure); return car > 0 ? true : false; } }
和
public IEnumerable GetEmployeeDetails(int employeeId) { using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) { return db.Query(anotherSp, new { EmployeeID = employeeId }, commandType: CommandType.StoredProcedure); } }
使用Generic Type T将这些添加到我的Base存储库的正确方法是什么,所以我可以返回任何类型的DTO或任何C#Native类型
当然,创建和部署Connection的function非常有用。
protected void Execute(Action 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(Func query) { using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) { return query.Invoke(db); } }
在您的呼叫站点中,只需编写您想要使用的逻辑。
public IEnumerable GetEmployeeDetails(int employeeId) { return Get(db => db.Query (anotherSp, new { EmployeeID = employeeId }, CommandType.StoredProcedure)); }
这与您的问题没有直接关系。 但我建议你考虑使用DapperExtensions。
最初,我确实使用Dapper实现了Repository模式。 缺点是,我必须全面写查询; 它非常古怪。 由于硬编码查询,几乎不可能编写通用存储库。
最近,我升级了我的代码以使用DapperExtensions。 这解决了很多问题。
以下是通用存储库:
public abstract class BaseRepository 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 (null); return result; } protected T GetById(Guid id) { var result = dapperExtensionsProxy.Get (id); return result; } protected T GetById(string id) { var result = dapperExtensionsProxy.Get (id); return result; } protected List GetList() { var result = dapperExtensionsProxy.GetList (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() }; var result = dapperExtensionsProxy.Delete(predicateGroup);//Send empty predicateGroup to delete all records. }
正如您在上面的代码中看到的,大多数方法只是基础DapperExtensionsProxy
类的包装。 DapperExtensionsProxy
内部还管理UnitOfWork,您可以在下面看到。 这两个类可以毫无问题地组合在一起。 我个人更喜欢将它们分开。
您还可以注意到,实现了其他方法Exists
, DeleteById
和DeleteAll
,这些方法不属于DapperExtensionsProxy
。
方法poco.SetDbId
在每个POCO类中定义以设置其Identifier属性。 就我而言,POCO的标识符可能具有不同的数据类型和名称。
以下是DapperExtensionsProxy
:
internal sealed class DapperExtensionsProxy { internal DapperExtensionsProxy(IUnitOfWork unitOfWork) { this.unitOfWork = unitOfWork; } IUnitOfWork unitOfWork = null; internal int Count(object predicate) where T : BasePoco { var result = unitOfWork.Connection.Count (predicate, unitOfWork.Transaction); return result; } internal T Get (object id) where T : BasePoco { var result = unitOfWork.Connection.Get (id, unitOfWork.Transaction); return result; } internal IEnumerable GetList (object predicate, IList sort = null, bool buffered = false) where T : BasePoco { var result = unitOfWork.Connection.GetList(predicate, sort, unitOfWork.Transaction, null, buffered); return result; } internal IEnumerable GetPage (object predicate, int page, int resultsPerPage, IList sort = null, bool buffered = false) where T : BasePoco { var result = unitOfWork.Connection.GetPage(predicate, sort, page, resultsPerPage, unitOfWork.Transaction, null, buffered); return result; } internal dynamic Insert (T poco) where T : BasePoco { var result = unitOfWork.Connection.Insert (poco, unitOfWork.Transaction); return result; } internal void Insert (IEnumerable listPoco) where T : BasePoco { unitOfWork.Connection.Insert (listPoco, unitOfWork.Transaction); } internal bool Update (T poco) where T : BasePoco { var result = unitOfWork.Connection.Update (poco, unitOfWork.Transaction); return result; } internal bool Delete (T poco) where T : BasePoco { var result = unitOfWork.Connection.Delete (poco, unitOfWork.Transaction); return result; } internal bool Delete (object predicate) where T : BasePoco { var result = unitOfWork.Connection.Delete (predicate, unitOfWork.Transaction); return result; } }
以下是BasePoco
使用的BasePoco
:
public abstract class BasePoco { Guid pocoId = Guid.NewGuid(); public Guid PocoId { get { return pocoId; } } public virtual void SetDbId(object id) {//Each POCO should override this method for specific implementation. throw new NotImplementedException("This method is not implemented by Poco."); } public override string ToString() { return PocoId + Environment.NewLine + base.ToString(); } }
这也使用UnitOfWork, 这里解释。