您好,欢迎访问一九零五行业门户网

详解C# .NET更智能的数据库操作的封装

前述:
对数据库操作的封装,相信网络上已经有一大堆,orm框架,或者是.net本身的ef,都很好的支持数据库操作。这篇文章是分享自己所思考的,对数据库操作的简单封装。我对于这篇文章,认为被浏览者所关注重点的是怎么分析设计数据库操作封装,代码是其次。而且,这是我第一篇文章,为了想好怎么实现花了些天,代码是博客发表时现写的。所以我想,使用可能还有bug,而且没有try catch异常的设计。
这个框架我理应做到对数据库无关,无论是哪个数据库都能够使用。不过,重点在于分析,而不是代码。所以,为了更好的阐述,我只做了对sql server的封装,对其他的话,浏览者可以自己设计;框架可支持链式写法,我想,在许多编程语言,大家对链式写法大不会陌生,所以我想,数据库访问也可以做成链式的模式。这个框架不需要写sql语句,对任何的操作,都只需要简单的传所需的参数,封装好对应的操作。
在阅读文章之前最好有些泛型、反射、link的基础,不然阅读可能会有些费劲。
进入重点:
框架的结构比较简单,使用简单工厂模式,因此笔者就不画一张uml图来解释,而用文字对里面方法进行描述。
在设计工厂接口时候,应该考虑接口中应该含有链式写法必须的三个阶段(也称部分):数据库基本操作(打开,关闭,创建等)、数据库的增删改查、数据库返回的数据(这里我做为执行阶段,估计大家会好奇为什么不是上一阶段,大家往下阅读就知道)和不是必须的事务操作。
using system; using system.collections.generic; using system.linq; using system.text; using system.data; using system.data.sqlclient; namespace dal { public interface dbhelper { /// <summary> /// 创建数据库连接 /// </summary> /// <param name="connectionstring">连接字符串</param> /// <returns></returns> dbhelper createconnection(string connectionstring); /// <summary> /// 打开数据库 /// </summary> /// <returns></returns> dbhelper openconnection(); /// <summary> /// 关闭数据库 /// </summary> /// <returns></returns> dbhelper closeconnection(); /// <summary> /// 释放sqlconnection对象 /// </summary> void disposedconnection(); /// <summary> /// 释放sqlcommand对象 /// </summary> void disposedcommand(); /// <summary> /// 创建sqlcommand对象 /// </summary> /// <returns></returns> dbhelper createcommand(); /// <summary> /// 设置sqlcommand的类型 /// </summary> /// <param name="type">commandtype枚举类型</param> /// <returns></returns> dbhelper setcommandtype(commandtype type); /// <summary> /// 要查询的表(多表以逗号隔开)、存储过程、视图名 /// </summary> /// <param name="name"></param> /// <returns></returns> dbhelper fromname(string name); /// <summary> /// 创建事务 /// </summary> /// <returns></returns> dbhelper begintransaction(); /// <summary> /// 事务回滚 /// </summary> /// <returns></returns> dbhelper transactionrowback(); /// <summary> /// 事务提交 /// </summary> /// <returns></returns> dbhelper transactioncommit(); /// <summary> /// 对多张表间的列进行联系 /// </summary> /// <param name="fields">表间联系的字段</param> /// <returns></returns> dbhelper formultable(string fields); /// <summary> /// 查询 /// </summary> /// <param name="fields">查询字段</param> /// <param name="where">查询条件字典</param> /// <param name="otherwhere">其他条件</param> /// <returns></returns> dbhelper select(string fields = "*", dictionary<string, object> where = null, string otherwhere = ""); /// <summary> /// 更新 /// </summary> /// <param name="model">需要更新的对象</param> /// <param name="where">更新条件</param> /// <param name="fields">更新字段</param> /// <param name="otherwhere">其他条件</param> /// <returns></returns> dbhelper update(object model, dictionary<string, object> where, string fields = "", string otherwhere = ""); /// <summary> /// 插入 /// </summary> /// <param name="model">需要插入的对象</param> /// <param name="fields">需要插入的字段</param> /// <returns></returns> dbhelper insert(object model, string fields = ""); /// <summary> /// 删除 /// </summary> /// <param name="where">删除条件</param> /// <param name="otherwhere">其他条件</param> /// <returns></returns> dbhelper delete(dictionary<string, object> where, string otherwhere = ""); /// <summary> /// 查询返回list /// </summary> /// <typeparam name="t">模型</typeparam> /// <returns></returns> list<t> tolist<t>() where t : class ,new(); /// <summary> /// 查询返回dataset /// </summary> /// <param name="datasetname"></param> /// <returns></returns> dataset todataset(string datasetname); /// <summary> /// 查询返回datatable /// </summary> /// <returns></returns> datatable todatatable(); /// <summary> /// 执行存储过程 /// </summary> /// <param name="parameter">存储过程参数</param> /// <returns></returns> dbhelper excuteproc(dictionary<string, object> parameter); /// <summary> /// 执行返回查询第一行第一列值 /// </summary> /// <returns></returns> object result(); /// <summary> /// 返回执行的影响行数 /// </summary> /// <returns></returns> int excuteresult(); /// <summary> /// 用户自定义sqlcommand /// </summary> /// <param name="fun">委托</param> void userdefineoperation(action<dynamic> fun); } }
好了,看完代码,大家对具体实现应该还是一头雾水,那,接下来一步步分析具体实现,是以sql server来分析。
在具体实现的类中sqlhelper,设计中所必须的字段。在一开始设计时候,我在想怎么给各个数据库兼容,因为它们使用的执行对象command是不同的,所以为了能够更好封装的库,将其设计sqlcommand不暴露给外部使用,而是在内部使用。暴露方法能够设置com的属性,以及excutename就存放着执行数据的对象。
//连接字符串 string connectionstring; //数据库连接对象 private sqlconnection conn; //执行对象 sqlcommand com; //表、存储过程、视图名称 string excutename; //事务 sqltransaction tran; //sql语句 stringbuilder sqlbuilderstring; //参数 sqlparameter[] paras;
第一部分:数据库基本操作
createconnection方法:这个方法其实就是new sqlconnection,对其赋值connectionstring,也采用了大家一般使用的单例模式,这样也会在执行的时候比较安全。不过这个单例是指一个helper对应一个sqlconnection,而不是设计为static,因为我觉得有些项目在访问的数据库有可能有多个。而且在创建时候,对其进行打开和关闭一次,为了检查能否真的能使用。
public dbhelper createconnection(string connectionstring) { if (!connectioncanuse()) { this.connectionstring = connectionstring; conn = new sqlconnection(this.connectionstring); } return this; } /// <summary> /// 检查conn是否能用 /// </summary> /// <returns></returns> public bool connectioncanuse() { if (conn == null) return false; try { conn.open(); conn.close(); }catch(exception e) { return false; } return true; }
打开、关闭、释放connection和创建command就不作解释了,因为里面就一句话。
关于基本操作,还有就是关于sqlcommandtype的设置,因为存储过程和普通的语句等操作字符串明显是不同,因此要写个方法来设置它。
第二部分:增删改查的操作 这里就解释为什么sql语句不是在这个阶段执行。我觉得,如果将具体的执行放在这个阶段,那么就会导致方法重载过多,为什么?因为并不是所有人都能考虑到使用者要返回的类型,比如我想要list,或者dataset等等,而且还会将这个方法的作用过重:在我设计的这些方法中,实操作的是对sql语句的生成,所以说为什么不能在这边执行,那么就不能重用。是吧,这样设计很灵活,将数据库真正执行放在下个阶段。而且这些方法都是链式的写法,所以会对执行能够很灵活的控制,最重要能够重用,不需要写别的重载方法,只需要一个方法。
生成sql语句在这也是简单的封装,如果要做起真的框架,我觉得sql字符串的组合还应该创建一个类,来更智能的组合用户的需求。
自然,里面使用到反射、linq。不过笔者也一步步解释,将怎么设计分享给大家。
大家看到select、insert、update、delete的接口都有where的条件字典。没错,反射就在这里使用。为了考虑到数据库的安全,所以sql自然只是简单的拼接,还应该使用参数。所以,反射就用在where生成参数上。大家也许还看到别的otherwhere,这个怎么不设计成参数,因为where能够实现的,其实就是赋值语句,也就是表内某字段 = 值,所以需要。在otherwhere中,存放的是其他特殊的条件。前面说这里设计的不完美,就因为如此,其实有些条件 like 或者 使用or ,虽然能够写在otherwhere中,但是没办法使用参数来控制。
那么接下来就是fiels参数了,这个在各个方法充当不同的作用。select是查询的字段,update中是更新的字段,在insert中是插入的字段,这样就灵活的控制了。在这些字段为空的时候,默认为全部,反射在这里就使用了,遍历模型对象中的属性,然后将它们一个个填进sql语句中。在这里比较注意的应该是插入,因为大家在写sql语句时候是这样的 insert tablename values(value,value....)这样的格式,这样是因为sql会自己对应值插入,而在程序中的模型类中,我想大家写属性可不是按顺序的吧,所以在反射遍历时候,就有可能将几个本来待在某个列位置的值去换了位置的情况。所以,这里在遍历的时候,应该按插入的完全格式来设计,也就是 insert tablename(field,field...) values(value,value...)。
在这几个方法中,delete最简单。
public dbhelper select(string fields = "*",dictionary<string,object> where = null,string otherwhere = "") { sqlbuilderstring = new stringbuilder(); sqlbuilderstring.appendline("select " + fields + " from " + this.excutename); list<sqlparameter> paras = new list<sqlparameter>(); sqlbuilderstring.appendline(" where 1 = 1 "); if (where != null && where.count > 0) { paras = new list<sqlparameter>(); //遍历where,将里面的条件添加到sqlparamter和sql语句中 where.keys.tolist().foreach(o => { sqlbuilderstring.appendline(" and "+ o + " = @" + o); paras.add(new sqlparameter(o, where[o])); }); } if(!string.isnullorempty(otherwhere)) { sqlbuilderstring.appendline(otherwhere); } this.paras = paras.toarray(); return this; }
public dbhelper update(object model,dictionary<string, object> where,string fields = "", string otherwhere = "") { type t = model.gettype(); list<string> keys = where.keys.tolist(); sqlbuilderstring = new stringbuilder(); bool firstnode = true; sqlbuilderstring.appendline("update "+excutename + " set "); list<sqlparameter> paras = new list<sqlparameter>(); if(string.isnullorempty(fields)) { t.getproperties().tolist().foreach(o => { if (!firstnode) sqlbuilderstring.append(","); else firstnode = false; if(!keys.contains(o.name)) { sqlbuilderstring.appendline(o.name + " = @"+o.name); paras.add(new sqlparameter(o.name,o.getvalue(model,null))); } }); }else { fields.split(',').tolist().foreach(o => { sqlbuilderstring.appendline(o + " = @" + o); paras.add(new sqlparameter(o, t.getproperty(o).getvalue(model, null))); }); } this.paras = paras.toarray(); return this; }
public dbhelper insert(object model,string fields = "") { list<sqlparameter> paras = new list<sqlparameter>(); type t = model.gettype(); sqlbuilderstring = new stringbuilder(); sqlbuilderstring.appendline("insert " + excutename); if(string.isnullorempty(fields)) { string s = ""; string s1=""; t.getproperties().tolist().foreach(o => { s += o.name + ","; s1 += " @" + o.name + ","; paras.add(new sqlparameter(o.name, o.getvalue(model, null))); }); s.remove(s.lastindexof(','),1); s1.remove(s.lastindexof(','), 1); sqlbuilderstring.appendline("(" + s + ")"); sqlbuilderstring.appendline(" values(" + s1 + ")"); }else { sqlbuilderstring.appendline("(" + fields + ")"); string s = ""; fields.split(',').tolist().foreach(o => { s += " @" + o + ","; paras.add(new sqlparameter(o, t.getproperty(o).getvalue(model, null))); }); sqlbuilderstring.appendline(" values(" + s + ")"); } this.paras = paras.toarray(); return this; }
public dbhelper delete(dictionary<string,object> where,string otherwhere = "") { sqlbuilderstring = new stringbuilder(); list<sqlparameter> paras = new list<sqlparameter>(); sqlbuilderstring.appendline("delete " + excutename); sqlbuilderstring.appendline(" where 1 = 1 "); where.keys.tolist().foreach(o => { sqlbuilderstring.appendline(" and " + o + " = @" + o); paras.add(new sqlparameter(o, where[o])); }); this.paras = paras.toarray(); return this; }
最后一个阶段,那就是执行阶段,这里封装了些执行的方法。
这个也是简单,最重要的方法应该是setcommand,这个方法是对sqlcommand进行设置,执行的语句,以及添加参数。
private void setcommand() { if(com.commandtype== commandtype.storedprocedure) { this.com.commandtext = excutename; }else { this.com.commandtext = sqlbuilderstring.tostring(); } this.paras.tolist().foreach(o => { this.com.parameters.add(o); }); }
其他就是执行的语句。
public list<t> tolist<t>() where t:class ,new() { list<t> list = new list<t>(); setcommand(); sqldatareader reader = com.executereader(); type t = typeof(t); list<propertyinfo> pros = t.getproperties().tolist(); while(reader.read()) { t model = new t(); pros.foreach(o => { o.setvalue(model, reader[o.name], null); }); list.add(model); } reader.dispose(); return list; } public dataset todataset(string datasetname = "") { dataset ds = new dataset(); setcommand(); sqldataadapter adapter = new sqldataadapter(com); adapter.fill(ds, string.isnullorempty(datasetname) ? this.excutename.replace(",", "_") : datasetname); adapter.dispose(); return ds; } public datatable todatatable() { datatable dt = new datatable(); setcommand(); sqldataadapter adapter = new sqldataadapter(com); adapter.fill(dt); adapter.dispose(); return dt; } public object result() { setcommand(); return com.executescalar(); } public int excuteresult() { setcommand(); return com.executenonquery(); } public dbhelper excuteproc(dictionary<string,object> parameter) { list<sqlparameter> paras = new list<sqlparameter>(); parameter.keys.tolist().foreach(o => { paras.add(new sqlparameter(o, parameter[o])); }); return this; }
当然,还不能少了让用户自定义的方法,所以最后还留了个方法,参数是委托。委托里面的参数还是动态类型,这就懂了吧,想用户怎么用,你就怎么定义。
public void userdefineoperation(action<dynamic> fun) { fun(this.com); }
好了,设计也就到这里,下面就贴上sqlhelper完整的代码。
using system;using system.collections.generic;using system.linq;using system.text;using system.data;using system.data.sqlclient;using system.reflection;namespace dal { public class sqlhelper:dbhelper { //连接字符串 string connectionstring; //数据库连接对象 private sqlconnection conn; //执行对象 sqlcommand com; //表、存储过程、视图名称 string excutename; //事务 sqltransaction tran; //sql语句 stringbuilder sqlbuilderstring; //参数 sqlparameter[] paras; private sqlhelper() { } /// <summary> /// 创建sqlhelper静态方法 /// </summary> /// <returns></returns> public static dbhelper getinstance() { return new sqlhelper(); } /// <summary> /// /// </summary> /// <param name="connectionstring"></param> /// <returns></returns> public dbhelper createconnection(string connectionstring) { if (!connectioncanuse()) { this.connectionstring = connectionstring; conn = new sqlconnection(this.connectionstring); } return this; } /// <summary> /// 检查conn是否能用 /// </summary> /// <returns></returns> public bool connectioncanuse() { if (conn == null) return false; try { conn.open(); conn.close(); }catch(exception e) { return false; } return true; } /// <summary> /// /// </summary> /// <returns></returns> public dbhelper openconnection() { if(conn.state != connectionstate.open) this.conn.open(); return this; } /// <summary> /// /// </summary> /// <returns></returns> public dbhelper closeconnection() { if(conn.state != connectionstate.closed) this.conn.close(); return this; } /// <summary> /// /// </summary> public void disposedconnection() { if (!connectionbeused()) this.conn.dispose(); } /// <summary> /// 检查数据库是否在被打开使用 /// </summary> /// <returns></returns> public bool connectionbeused() { if(conn.state == connectionstate.open) return true; return false; } /// <summary> /// /// </summary> /// <returns></returns> public dbhelper createcommand() { if (this.com == null) { this.com = new sqlcommand(); com.connection = this.conn; } return this; } /// <summary> /// /// </summary> public void disposedcommand() { this.com.dispose(); } /// <summary> /// /// </summary> /// <param name="type"></param> /// <returns></returns> public dbhelper setcommandtype(commandtype type) { this.com.commandtype = type; return this; } /// <summary> /// /// </summary> /// <param name="name"></param> /// <returns></returns> public dbhelper fromname(string name) { this.excutename = name; return this; } /// <summary> /// /// </summary> /// <returns></returns> public dbhelper begintransaction() { this.tran = conn.begintransaction(); com.transaction = this.tran; return this; } /// <summary> /// /// </summary> /// <returns></returns> public dbhelper transactionrowback() { if(tran!=null) { tran.rollback(); } return this; } /// <summary> /// /// </summary> /// <returns></returns> public dbhelper transactioncommit() { if(tran!=null) { tran.commit(); tran = null; } return this; } /// <summary> /// /// </summary> /// <param name="fields"></param> /// <param name="where"></param> /// <param name="otherwhere"></param> /// <returns></returns> public dbhelper select(string fields = "*",dictionary<string,object> where = null,string otherwhere = "") { sqlbuilderstring = new stringbuilder(); sqlbuilderstring.appendline("select " + fields + " from " + this.excutename); list<sqlparameter> paras = new list<sqlparameter>(); sqlbuilderstring.appendline(" where 1 = 1 "); if (where != null && where.count > 0) { paras = new list<sqlparameter>(); //遍历where,将里面的条件添加到sqlparamter和sql语句中 where.keys.tolist().foreach(o => { sqlbuilderstring.appendline(" and "+ o + " = @" + o); paras.add(new sqlparameter(o, where[o])); }); } if(!string.isnullorempty(otherwhere)) { sqlbuilderstring.appendline(otherwhere); } this.paras = paras.toarray(); return this; } public dbhelper formultable(string fields) { list<string> tables = excutename.split(',').tolist(); fields.split(',').tolist().foreach(o => { for(int i = 0;i<tables.count-2;i++) { sqlbuilderstring.appendline(" and " + tables[i] + "." + o + " = " + tables[i + 1] + "." + o); } }); return this; } public dbhelper update(object model,dictionary<string, object> where,string fields = "", string otherwhere = "") { type t = model.gettype(); list<string> keys = where.keys.tolist(); sqlbuilderstring = new stringbuilder(); bool firstnode = true; sqlbuilderstring.appendline("update "+excutename + " set "); list<sqlparameter> paras = new list<sqlparameter>(); if(string.isnullorempty(fields)) { t.getproperties().tolist().foreach(o => { if (!firstnode) sqlbuilderstring.append(","); else firstnode = false; if(!keys.contains(o.name)) { sqlbuilderstring.appendline(o.name + " = @"+o.name); paras.add(new sqlparameter(o.name,o.getvalue(model,null))); } }); }else { fields.split(',').tolist().foreach(o => { sqlbuilderstring.appendline(o + " = @" + o); paras.add(new sqlparameter(o, t.getproperty(o).getvalue(model, null))); }); } this.paras = paras.toarray(); return this; } public dbhelper insert(object model,string fields = "") { list<sqlparameter> paras = new list<sqlparameter>(); type t = model.gettype(); sqlbuilderstring = new stringbuilder(); sqlbuilderstring.appendline("insert " + excutename); if(string.isnullorempty(fields)) { string s = ""; string s1=""; t.getproperties().tolist().foreach(o => { s += o.name + ","; s1 += " @" + o.name + ","; paras.add(new sqlparameter(o.name, o.getvalue(model, null))); }); s.remove(s.lastindexof(','),1); s1.remove(s.lastindexof(','), 1); sqlbuilderstring.appendline("(" + s + ")"); sqlbuilderstring.appendline(" values(" + s1 + ")"); }else { sqlbuilderstring.appendline("(" + fields + ")"); string s = ""; fields.split(',').tolist().foreach(o => { s += " @" + o + ","; paras.add(new sqlparameter(o, t.getproperty(o).getvalue(model, null))); }); sqlbuilderstring.appendline(" values(" + s + ")"); } this.paras = paras.toarray(); return this; } public dbhelper delete(dictionary<string,object> where,string otherwhere = "") { sqlbuilderstring = new stringbuilder(); list<sqlparameter> paras = new list<sqlparameter>(); sqlbuilderstring.appendline("delete " + excutename); sqlbuilderstring.appendline(" where 1 = 1 "); where.keys.tolist().foreach(o => { sqlbuilderstring.appendline(" and " + o + " = @" + o); paras.add(new sqlparameter(o, where[o])); }); this.paras = paras.toarray(); return this; } private void setcommand() { if(com.commandtype== commandtype.storedprocedure) { this.com.commandtext = excutename; }else { this.com.commandtext = sqlbuilderstring.tostring(); } this.paras.tolist().foreach(o => { this.com.parameters.add(o); }); } public list<t> tolist<t>() where t:class ,new() { list<t> list = new list<t>(); setcommand(); sqldatareader reader = com.executereader(); type t = typeof(t); list<propertyinfo> pros = t.getproperties().tolist(); while(reader.read()) { t model = new t(); pros.foreach(o => { o.setvalue(model, reader[o.name], null); }); list.add(model); } reader.dispose(); return list; } public dataset todataset(string datasetname = "") { dataset ds = new dataset(); setcommand(); sqldataadapter adapter = new sqldataadapter(com); adapter.fill(ds, string.isnullorempty(datasetname) ? this.excutename.replace(",", "_") : datasetname); adapter.dispose(); return ds; } public datatable todatatable() { datatable dt = new datatable(); setcommand(); sqldataadapter adapter = new sqldataadapter(com); adapter.fill(dt); adapter.dispose(); return dt; } public object result() { setcommand(); return com.executescalar(); } public int excuteresult() { setcommand(); return com.executenonquery(); } public dbhelper excuteproc(dictionary<string,object> parameter) { list<sqlparameter> paras = new list<sqlparameter>(); parameter.keys.tolist().foreach(o => { paras.add(new sqlparameter(o, parameter[o])); }); return this; } public void userdefineoperation(action<dynamic> fun) { fun(this.com); } } }
最后还有两个事务的方法,前面忘记说了,其实就是sqltransaction,在里面的sqlcommand加上这个,就可以实现。或许有人会问,如果有同一时间段有好几个sqlcommand怎么办?不会的,sqlcommand我也设置成单例,就不会发生控制不了的事情了。
结束语:第一次的博客,我虽然做过不少“幼稚作品”,毕竟我是大三学生,如果随意的写文章,我担心只是会成为被嘲笑的对象,幼稚的“作品”也不好意思放在网上给大家看。所以,在想了几天,写了我觉得蛮有用的封装,虽然可能对许多项目不起作用,但是读者可以自己在更深的思考。
这个框架,我觉得应该还能更好的封装,比如从sql语句组合,调用的时候发生异常处理,怎么更好的实现链式组合,多数据库的处理控制,加上锁我觉得也是可以,毕竟做web的时候可不是像winform,每个端都有自己的connection。还有一个我觉得不错的,就是在模型上做处理,加上特性,让框架能够识别主键,外键,在程序中建立sql中的联系等。那么就给读者思考了。
以上就是详解c# .net更智能的数据库操作的封装的详细内容。
其它类似信息

推荐信息