目录
- 分享基于ef6、unitwork、autofac的repository模式设计
- 二、repository设计具体的实现代码
- 三、repository设计的具体的使用
分享基于ef6、unitwork、autofac的repository模式设计
一、实现的思路和结构图
- repository的共同性
有一些公共的方法(增删改查), 这些方法无关于repository操作的是哪个实体类,可以把这些方法定义成接口irepository ,然后有个基类baserepository 实现该接口的方法。常见的方法,比如find, filter, delete, create等
- repository的差异性
每个repository类又会有一些差异性,应当允许它们能够继承baserepository 之外,还能够再扩展自己的一些方法。所以每个类都可以再定义一个自己特有的接口,定义一些属于自己repository的方法。
-
repository的协同性
不同的repository可能需要协同,repository对数据的修改,需要在统一的保存.
最终实现的类结构图如下:
二、repository设计具体的实现代码
irepository 接口定义了repository共有的方法, baserepository 实现了这些接口的方法。其它的repository类再集成baserepository 方法,就天然的得到了对数据操作的基本方法。
- irepository 代码
public interface irepository<tentity> where tentity : class { /// <summary> /// gets all objects from database /// </summary> /// <returns></returns> iqueryable<tentity> all(); /// <summary> /// gets objects from database by filter. /// </summary> /// <param name="predicate">specified a filter</param> /// <returns></returns> iqueryable<tentity> filter(expression<func<tentity, bool>> predicate); /// <summary> /// gets objects from database with filtering and paging. /// </summary> /// <param name="filter">specified a filter</param> /// <param name="total">returns the total records count of the filter.</param> /// <param name="index">specified the page index.</param> /// <param name="size">specified the page size</param> /// <returns></returns> iqueryable<tentity> filter(expression<func<tentity, bool>> filter, out int total, int index = 0, int size = 50); /// <summary> /// gets the object(s) is exists in database by specified filter. /// </summary> /// <param name="predicate">specified the filter expression</param> /// <returns></returns> bool contains(expression<func<tentity, bool>> predicate); /// <summary> /// find object by keys. /// </summary> /// <param name="keys">specified the search keys.</param> /// <returns></returns> tentity find(params object[] keys); /// <summary> /// find object by specified expression. /// </summary> /// <param name="predicate"></param> /// <returns></returns> tentity find(expression<func<tentity, bool>> predicate); /// <summary> /// create a new object to database. /// </summary> /// <param name="t">specified a new object to create.</param> /// <returns></returns> void create(tentity t); /// <summary> /// delete the object from database. /// </summary> /// <param name="t">specified a existing object to delete.</param> void delete(tentity t); /// <summary> /// delete objects from database by specified filter expression. /// </summary> /// <param name="predicate"></param> /// <returns></returns> int delete(expression<func<tentity, bool>> predicate); /// <summary> /// update object changes and save to database. /// </summary> /// <param name="t">specified the object to save.</param> /// <returns></returns> void update(tentity t); /// <summary> /// select single item by specified expression. /// </summary> /// <param name="expression"></param> /// <returns></returns> tentity firstordefault(expression<func<tentity, bool>> expression); }
- baserepository 代码
public class baserepository<tentity> : irepository<tentity> where tentity : class { protected readonly dbcontext context; public baserepository(dbcontext context) { context = context; } /// <summary> /// gets all objects from database /// </summary> /// <returns></returns> public iqueryable<tentity> all() { return context.set<tentity>().asqueryable(); } /// <summary> /// gets objects from database by filter. /// </summary> /// <param name="predicate">specified a filter</param> /// <returns></returns> public virtual iqueryable<tentity> filter(expression<func<tentity, bool>> predicate) { return context.set<tentity>().where<tentity>(predicate).asqueryable<tentity>(); } /// <summary> /// gets objects from database with filtering and paging. /// </summary> /// <param name="filter">specified a filter</param> /// <param name="total">returns the total records count of the filter.</param> /// <param name="index">specified the page index.</param> /// <param name="size">specified the page size</param> /// <returns></returns> public virtual iqueryable<tentity> filter(expression<func<tentity, bool>> filter, out int total, int index = 0, int size = 50) { var skipcount = index * size; var resetset = filter != null ? context.set<tentity>().where<tentity>(filter).asqueryable() : context.set<tentity>().asqueryable(); resetset = skipcount == 0 ? resetset.take(size) : resetset.skip(skipcount).take(size); total = resetset.count(); return resetset.asqueryable(); } /// <summary> /// gets the object(s) is exists in database by specified filter. /// </summary> /// <param name="predicate">specified the filter expression</param> /// <returns></returns> public bool contains(expression<func<tentity, bool>> predicate) { return context.set<tentity>().any(predicate); } /// <summary> /// find object by keys. /// </summary> /// <param name="keys">specified the search keys.</param> /// <returns></returns> public virtual tentity find(params object[] keys) { return context.set<tentity>().find(keys); } /// <summary> /// find object by specified expression. /// </summary> /// <param name="predicate"></param> /// <returns></returns> public virtual tentity find(expression<func<tentity, bool>> predicate) { return context.set<tentity>().firstordefault<tentity>(predicate); } /// <summary> /// create a new object to database. /// </summary> /// <param name="t">specified a new object to create.</param> /// <returns></returns> public virtual void create(tentity t) { context.set<tentity>().add(t); } /// <summary> /// delete the object from database. /// </summary> /// <param name="t">specified a existing object to delete.</param> public virtual void delete(tentity t) { context.set<tentity>().remove(t); } /// <summary> /// delete objects from database by specified filter expression. /// </summary> /// <param name="predicate"></param> /// <returns></returns> public virtual int delete(expression<func<tentity, bool>> predicate) { var objects = filter(predicate); foreach (var obj in objects) context.set<tentity>().remove(obj); return context.savechanges(); } /// <summary> /// update object changes and save to database. /// </summary> /// <param name="t">specified the object to save.</param> /// <returns></returns> public virtual void update(tentity t) { try { var entry = context.entry(t); context.set<tentity>().attach(t); entry.state = entitystate.modified; } catch (optimisticconcurrencyexception ex) { throw ex; } } /// <summary> /// select single item by specified expression. /// </summary> /// <param name="expression"></param> /// <returns></returns> public tentity firstordefault(expression<func<tentity, bool>> expression) { return all().firstordefault(expression); } }
iunitofwork接口定义了方法获取特定的repository, 执行存储过程, savechange方法提交修改,统一更新数据。
- iunitofwork接口代码:
public interface iunitofwork : idisposable { dbcontext dbcontext { get; } trepository getrepository<trepository>() where trepository : class; void executeprocedure(string procedurecommand, params object[] sqlparams); void executesql(string sql); list<t> sqlquery<t>(string sql); void savechanges(); }
unitofwork代码, 代码中使用到了autofac中的icomponentcontext来获取repository实例
public class unitofwork : iunitofwork { private readonly icomponentcontext _componentcontext; protected readonly dbcontext context; public unitofwork(dbcontext context, icomponentcontext componentcontext) { context = context; _componentcontext = componentcontext; } public dbcontext dbcontext => context; public trepository getrepository<trepository>() where trepository : class { return _componentcontext.resolve<trepository>(); } public void executeprocedure(string procedurecommand, params object[] sqlparams) { context.database.executesqlcommand(procedurecommand, sqlparams); } public void executesql(string sql) { context.database.executesqlcommand(sql); } public list<t> sqlquery<t>(string sql) { return context.database.sqlquery<t>(sql).tolist(); } public void savechanges() { try { context.savechanges(); } catch (invalidoperationexception ex) { if (!ex.message.contains("the changes to the database were committed successfully")) { throw; } } } public void dispose() { context?.dispose(); } }
三、repository设计的具体的使用
这里我们定义一个
istudentrepository
接口, 包含了方法getallstudents()
, 同时继承于irepository<student>
接口
public interface istudentrepository : irepository<student> { ienumerable<dynamic> getallstudents(); }
接着定义studentrepository类来实现这个接口
public class studentrepository : baserepository<student>, istudentrepository { private readonly schoolcontext _context; public studentrepository(schoolcontext context) : base(context) { _context = context; } public ienumerable<dynamic> getallstudents() { return _context.students; } }
- 在
application_start
方法中使用autofac注册repository的代码如下:
var builder = new containerbuilder(); //register controllers builder.registercontrollers(typeof(mvcapplication).assembly); //register repository builder.registerassemblytypes(assembly.getexecutingassembly()).asimplementedinterfaces(); //add the entity framework context to make sure only one context per request builder.registertype<schoolcontext>().instanceperrequest(); builder.register(c => c.resolve<schoolcontext>()).as<dbcontext>().instanceperrequest(); var container = builder.build(); dependencyresolver.setresolver(new autofacdependencyresolver(container));
- 在控制器中注入使用repository的代码如下:
private readonly iunitofwork _repositorycenter; private readonly istudentrepository _studentrepository; public homecontroller(iunitofwork repositorycenter) { _repositorycenter = repositorycenter; _studentrepository = _repositorycenter.getrepository<istudentrepository>(); } public actionresult index(student sessionstudent) { var students = _studentrepository.getallstudents(); // 同时你也可以使用定义于irepository<student>中的方法, 比如: _studentrepository.delete(students.first()); _repositorycenter.savechanges(); ... return view(students); }
四、思路总结
上面的设计,把repository的通用代码剥离到父类中,同时又允许每个repository扩展自己的方法,达到了比较理想的状态。
只是现在的设计和autofac耦合了,但是如果想继续剥离autofac直接使用
_repositorycenter.getrepository<istudentrepository>();
的方式获取istudentrepository的实例就很困难了。
五、案例源码
源代码仓库 autofacmvc