在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前述: 对数据库操作的封装,相信网络上已经有一大堆,ORM框架,或者是.NET本身的EF,都很好的支持数据库操作。这篇文章是分享自己所思考的,对数据库操作的简单封装。我对于这篇文章,认为被浏览者所关注重点的是怎么分析设计数据库操作封装,代码是其次。而且,这是我第一篇文章,为了想好怎么实现花了些天,代码是博客发表时现写的。所以我想,使用可能还有bug,而且没有try catch异常的设计。 这个框架我理应做到对数据库无关,无论是哪个数据库都能够使用。不过,重点在于分析,而不是代码。所以,为了更好的阐述,我只做了对sql Server的封装,对其他的话,浏览者可以自己设计;框架可支持链式写法,我想,在许多编程语言,大家对链式写法大不会陌生,所以我想,数据库访问也可以做成链式的模式。这个框架不需要写sql语句,对任何的操作,都只需要简单的传所需的参数,封装好对应的操作。 在阅读文章之前最好有些泛型、反射、Link的基础,不然阅读可能会有些费劲。
进入重点: 框架的结构比较简单,使用简单工厂模式,因此笔者就不画一张UML图来解释,而用文字对里面方法进行描述。 在设计工厂接口时候,应该考虑接口中应该含有链式写法必须的三个阶段(也称部分):数据库基本操作(打开,关闭,创建等)、数据库的增删改查、数据库返回的数据(这里我做为执行阶段,估计大家会好奇为什么不是上一阶段,大家往下阅读就知道)和不是必须的事务操作。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data; 6 using System.Data.SqlClient; 7 8 namespace Dal 9 { 10 public interface DbHelper 11 { 12 /// <summary> 13 /// 创建数据库连接 14 /// </summary> 15 /// <param name="connectionString">连接字符串</param> 16 /// <returns></returns> 17 DbHelper createConnection(string connectionString); 18 19 /// <summary> 20 /// 打开数据库 21 /// </summary> 22 /// <returns></returns> 23 DbHelper openConnection(); 24 25 /// <summary> 26 /// 关闭数据库 27 /// </summary> 28 /// <returns></returns> 29 DbHelper closeConnection(); 30 31 /// <summary> 32 /// 释放sqlConnection对象 33 /// </summary> 34 void DisposedConnection(); 35 36 /// <summary> 37 /// 释放sqlCommand对象 38 /// </summary> 39 void DisposedCommand(); 40 41 /// <summary> 42 /// 创建sqlCommand对象 43 /// </summary> 44 /// <returns></returns> 45 DbHelper createCommand(); 46 47 /// <summary> 48 /// 设置sqlCommand的类型 49 /// </summary> 50 /// <param name="type">CommandType枚举类型</param> 51 /// <returns></returns> 52 DbHelper setCommandType(CommandType type); 53 54 /// <summary> 55 /// 要查询的表(多表以逗号隔开)、存储过程、视图名 56 /// </summary> 57 /// <param name="Name"></param> 58 /// <returns></returns> 59 DbHelper FromName(string Name); 60 61 /// <summary> 62 /// 创建事务 63 /// </summary> 64 /// <returns></returns> 65 DbHelper beginTransaction(); 66 67 /// <summary> 68 /// 事务回滚 69 /// </summary> 70 /// <returns></returns> 71 DbHelper TransactionRowBack(); 72 73 /// <summary> 74 /// 事务提交 75 /// </summary> 76 /// <returns></returns> 77 DbHelper TransactionCommit(); 78 79 /// <summary> 80 /// 对多张表间的列进行联系 81 /// </summary> 82 /// <param name="Fields">表间联系的字段</param> 83 /// <returns></returns> 84 DbHelper ForMulTable(string Fields); 85 86 /// <summary> 87 /// 查询 88 /// </summary> 89 /// <param name="Fields">查询字段</param> 90 /// <param name="Where">查询条件字典</param> 91 /// <param name="otherWhere">其他条件</param> 92 /// <returns></returns> 93 DbHelper Select(string Fields = "*", Dictionary<string, object> Where = null, string otherWhere = ""); 94 95 /// <summary> 96 /// 更新 97 /// </summary> 98 /// <param name="model">需要更新的对象</param> 99 /// <param name="Where">更新条件</param> 100 /// <param name="Fields">更新字段</param> 101 /// <param name="otherWhere">其他条件</param> 102 /// <returns></returns> 103 DbHelper Update(object model, Dictionary<string, object> Where, string Fields = "", string otherWhere = ""); 104 105 /// <summary> 106 /// 插入 107 /// </summary> 108 /// <param name="model">需要插入的对象</param> 109 /// <param name="Fields">需要插入的字段</param> 110 /// <returns></returns> 111 DbHelper Insert(object model, string Fields = ""); 112 113 /// <summary> 114 /// 删除 115 /// </summary> 116 /// <param name="Where">删除条件</param> 117 /// <param name="otherWhere">其他条件</param> 118 /// <returns></returns> 119 DbHelper Delete(Dictionary<string, object> Where, string otherWhere = ""); 120 121 122 /// <summary> 123 /// 查询返回List 124 /// </summary> 125 /// <typeparam name="T">模型</typeparam> 126 /// <returns></returns> 127 List<T> ToList<T>() 128 where T : class ,new(); 129 130 131 /// <summary> 132 /// 查询返回DataSet 133 /// </summary> 134 /// <param name="DatasetName"></param> 135 /// <returns></returns> 136 DataSet ToDataSet(string DatasetName); 137 138 /// <summary> 139 /// 查询返回DataTable 140 /// </summary> 141 /// <returns></returns> 142 DataTable ToDataTable(); 143 144 /// <summary> 145 /// 执行存储过程 146 /// </summary> 147 /// <param name="Parameter">存储过程参数</param> 148 /// <returns></returns> 149 DbHelper ExcuteProc(Dictionary<string, object> Parameter); 150 151 /// <summary> 152 /// 执行返回查询第一行第一列值 153 /// </summary> 154 /// <returns></returns> 155 object Result(); 156 157 /// <summary> 158 /// 返回执行的影响行数 159 /// </summary> 160 /// <returns></returns> 161 int ExcuteResult(); 162 163 /// <summary> 164 /// 用户自定义sqlCommand 165 /// </summary> 166 /// <param name="fun">委托</param> 167 void UserDefineOperation(Action<dynamic> fun); 168 } 169 }
好了,看完代码,大家对具体实现应该还是一头雾水,那,接下来一步步分析具体实现,是以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() { |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论