在 .NET 的学习过程当中用过三种数据库:Sql Server、Access、SQLite。Sql Server 用得相对多一点点,可是,麻烦,每次用它都须要开服务,并且还费资源,更麻烦的是拷贝到一台没有装SqlServer的电脑上还不能用,因此通常练习中只要不涉及到什么存储过程这些也基本不用它。Access简单的文件型数据库,office的套件,基本的SQL语句也支持,在通常的小型应用系统中用起来还行,并且在Windows 7开始操做系统中就已经内置了Access的服务,随便拷贝。SQLite用过一次,须要安装服务,和Access同样,拷贝一下就能够用,就是管理工具的界面比较shit。sql
用过三种数据库后,发现其实在微软的ADO.NET中操做数据库的方式是同样的,连接对象都是继承自实现了IDbConnection接口的抽象类DbConnection,以及实现了IDbCommand的接口的抽象类DbCommand,其它的都同样。古就能够统一用接口或者抽象来来进行操做数据库了,这样就可以实现基本的跨数据库访问的功能了。惟一不一样的就是实例化链接字符串的时候要用具体的子类来进行实例化,这时就能够用静态工厂来实现,从而作到不修改程序就实现夸数据库的功能,固然应用程序中的SQL语句是通用的才行,若是是某些数据库特有的,那就不行了。数据库
也成为简单工厂,主要是用来因对对象的变化,根据不一样的要求建立不一样的实例,就能够进行一个不一样的操做。在黑马的基础增强里面,有一个比较经典的例子可以说明他的做用:简单计算器。主程序中一直不变,变的是运算规则,而后把运算规则给抽出来,写到一个工厂中,而后根据不一样的运算符来建立不一样的计算实例,而后用一个同一的接口返回这个实例对象,就可以完成计算了。数组
1 /// <summary>
2 /// 根据传递过来的运算符,来创造不一样的子类对象 3 /// </summary>
4 /// <param name="op">运算符</param>
5 /// <returns>返回的是一个实现了接口ICalculatorable.ICalculable的子类对象</returns>
6 public static ICalculatorable.ICalculable CreatInstance (string op) 7 { 8 //建立一个接口变量,赋值为null
9 ICalculatorable.ICalculable cc = null; 10 ////根据传递过来的运算符来建立对应的实例成员
11 switch (op) 12 { 13 //若是传递过来的是加法运算符,就建立一个计算加法的实例成员,而且赋值给上面的接口变量
14 case "+": cc = new AddLib_02.Add(); break; 15 case "-": cc = new SubLib_02.Sub(); break; 16 case "*": cc = new MultipLib_02.Multip(); break; 17 case "/": cc = new DivLib_02.Div(); break; 18 case "%": cc = new ModelLib_02.Model(); break; 19 //若上面的都不符合,就将接口变量赋值为null
20 default: cc = null; 21 break; 22 } 23 //返回指向了一个具体实例对象的接口变量
24 return cc; 25 }
其实在访问数据库的时候也是同样的,惟一变的就是连接实例,因此就能够把建立实例的东西给抽出来,交给一个工厂来实现,工厂能够经过读取配置文件来进行建立相应的实例对象,而后返回。代码以下:oracle
1 internal sealed class DbProvideFactory 2 { 3 private static string dbProvide = ConfigurationManager.AppSettings["provide"]; 4 public static string DbProvide { get { return dbProvide; } } 5
6 private static string connectionString = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; 7 /// <summary>
8 /// 链接字符串 9 /// </summary>
10 public static string ConnectionString 11 { 12 get { return connectionString; } 13 private set { connectionString = value; } 14 } 15
16
17 #region 返回一个链接对象
18 public static DbConnection GetConnection () 19 { 20 switch (dbProvide.ToLower()) 21 { 22 case "sqlserver": 23 case "sql": 24 case "mssqlserver": 25 case "mssql": return new SqlConnection(connectionString); 26 case "access": return new OleDbConnection(connectionString); 27 case "sqlite": return new SQLiteConnection(connectionString); 28 case "oracle": return new OracleConnection(connectionString); 29 default: return new SqlConnection(connectionString); 30 } 31 } 32 #endregion
33
34 #region 返回一个 Adapter
35 /// <summary>
36 /// 返回一个 Adapter 37 /// </summary>
38 /// <param name="cmd">Command 命令</param>
39 /// <returns></returns>
40 public static DataAdapter GetAdapter (IDbCommand cmd) 41 { 42 switch (dbProvide.ToLower()) 43 { 44 case "sqlserver": 45 case "sql": 46 case "mssqlserver": 47 case "mssql": return new SqlDataAdapter(cmd as SqlCommand); 48 case "access": return new OleDbDataAdapter(cmd as OleDbCommand); 49 case "sqlite": return new SQLiteDataAdapter(cmd as SQLiteCommand); 50 case "oracle": return new OracleDataAdapter(cmd as OracleCommand); 51 default: return new SqlDataAdapter(cmd as SqlCommand); 52 } 53 } 54 #endregion
55
56 #region 生成参数化查询时的参数对象
57 /// <summary>
58 /// 生成参数化查询时的参数对象 59 /// </summary>
60 /// <param name="name">参数名,@符号无关紧要</param>
61 ///
62 /// <returns></returns>
63 public static DbParameter GetParameter (string name, object value) 64 { 65 if (!name.StartsWith("@")) 66 { 67 name = "@" + name; 68 } 69 if (value == null) 70 { 71 value = DBNull.Value; 72 } 73 switch (dbProvide.ToLower()) 74 { 75 case "sqlserver": 76 case "sql": 77 case "mssqlserver": 78 case "mssql": return new SqlParameter(name, value); 79 case "access": return new OleDbParameter(name, value); 80 case "sqlite": return new SQLiteParameter(name, value); 81 case "oracle": return new OracleParameter(name, value); 82 default: return new SqlParameter(name, value); 83 } 84 } 85
86 /// <summary>
87 /// 生成参数化查询时的 输出参数 88 /// </summary>
89 /// <param name="name">参数名</param>
90 /// <param name="type">参数类型</param>
91 /// <returns></returns>
92 public static IDbDataParameter GetParameter (string name,DbType type) 93 { 94 if (!name.StartsWith("@")) 95 { 96 name = "@" + name; 97 } 98 IDbDataParameter p; 99 switch (dbProvide.ToLower()) 100 { 101 case "sqlserver": 102 case "sql": 103 case "mssqlserver": 104 case "mssql": p = new SqlParameter(name, type); break; 105 case "access": p = new OleDbParameter(name, type); break; 106 case "sqlite": p = new SQLiteParameter(name, type); break; 107 case "oracle": p = new OracleParameter(name, type); break; 108 default: p = new SqlParameter(name, type); break; 109 } 110 p.Direction = ParameterDirection.Output; 111 return p; 112 } 113 #endregion
114 }
上面代码中首先读取了配置文件,以及链接字符串,这点可能封装的是太好,按理说,应该从DbHelper中传递过来,可是考虑到后面多处用到它,就把他放到这个地方了。
根据配置文件中数据提供者的值不一样而后建立不一样的数据库链接对象。同时因为在参数化查询的时候,须要实例化参数对象,有封装了两个参数对象方法。在用SqlServer的存储过程当中,考虑到有输出参数,古作了一个重载。ide
至于 DataAdapter,因为它里也是能够本身建立链接的,因此也就比较麻烦一点,手动建立了,其实能够利用微软已经提供好的一种方式,就是经过读取配置文件中的配置提供者,而后调用一些方法来进行建立,实现原理中应该也是利用的反射,因为这个值在配置文件中比较麻烦,须要写全称,也就放弃它了。工具
上面基本上就是利用简单工厂来实现的建立不一样数据库实例和参数化对象的方式。sqlserver
SQLite须要在官方下载ADO.NET的插件,而后进行引用,Oracle数据库没有尝试过,也须要应用组件,列在此处也是为了之后方便扩展学习
这里面就封装了几个常规的增删改查的方法,调用工厂的方法获得链接对象和参数,而后用接口去指向他们,因为使用的是接口去指向,故在添加参数的时候,只可以经过遍从来实现,能够改成抽象类来指向,这样就可以一次添加一个数组了。代码以下:spa
1 public partial class DbHelper 2 { 3 //#region 获得一个链接对象,由具体的子类去重写而实现
4 ///// <summary>
5 ///// 获得一个链接对象,由具体的子类去重写而实现 6 ///// </summary>
7 ///// <returns></returns>
8 //public abstruct DbConnection GetConnection (); 9 //#endregion
10
11 #region 执行增长、删除、更新三个非查询语句
12 public static int ExecuteNonQuery (string strSql, params IDataParameter[] pars) 13 { 14 using (IDbConnection conn = DbProvideFactory.GetConnection() ) 15 { 16 using (IDbCommand cmd = conn.CreateCommand()) 17 { 18 cmd.CommandText = strSql; 19
20 if (pars != null && pars.Length > 0) 21 { 22 foreach (var item in pars) 23 { 24 cmd.Parameters.Add(item); 25 } 26 } 27 conn.Open(); 28 return cmd.ExecuteNonQuery(); 29 } 30 } 31 } 32 #endregion
33
34 #region 执行标量值查询
35 public static object ExecuteScalar (string strSql, params IDataParameter[] pars) 36 { 37 using (IDbConnection conn = DbProvideFactory.GetConnection()) 38 { 39 using (IDbCommand cmd = conn.CreateCommand()) 40 { 41 cmd.CommandText = strSql; 42 if (pars != null && pars.Length > 0) 43 { 44 foreach (var item in pars) 45 { 46 cmd.Parameters.Add(item); 47 } 48 } 49 conn.Open(); 50 return cmd.ExecuteScalar(); 51 } 52 } 53 } 54 #endregion
55
56 #region 返回一个只读的DbDataReader
57 public static IDataReader ExecuteReader (string strSql,CommandType type, params IDataParameter[] pars) 58 { 59 IDbConnection conn = DbProvideFactory.GetConnection(); 60
61 using (IDbCommand cmd = conn.CreateCommand()) 62 { 63 cmd.CommandText = strSql; 64 if (pars != null && pars.Length > 0) 65 { 66 foreach (var item in pars) 67 { 68 cmd.Parameters.Add(item); 69 } 70 } 71 cmd.CommandType = type; 72 try
73 { 74 conn.Open(); 75 //外部关闭当前链接
76 return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection); 77 } 78 catch
79 { 80 conn.Close(); 81 conn.Dispose(); 82 throw; 83 } 84 } 85 } 86 #endregion
87
88 #region 返回一张数据表格,若是没有查到数据就返回null
89 //因为用的是接口(或者抽象类)不可以直接建立Command来添加参数,因此调用了工厂的方法建立一个链接对象,而后建立Command来实现参数的添加
90 public static DataTable DataAdpater (string strSql, params IDataParameter[] pars) 91 { 92 using (IDbConnection conn = DbProvideFactory.GetConnection()) 93 { 94 IDbCommand cmd = conn.CreateCommand(); 95 cmd.CommandText = strSql; 96 if (pars != null && pars.Length > 0) 97 { 98 foreach (var item in pars) 99 { 100 cmd.Parameters.Add(item); 101 } 102 } 103 IDataAdapter adapter = DbProvideFactory.GetAdapter(cmd); 104 DataSet set = new DataSet(); 105 int count = adapter.Fill(set); 106 return count > 0 ? set.Tables[0] : null; 107 } 108 } 109 #endregion
110
111 #region 将一个SqlDataReader对象转换成一个实体类对象 +static TEntity MapEntity<TEntity>(SqlDataReader reader) where TEntity : class,new()
112 /// <summary>
113 /// 将一个DataReader对象转换成一个实体类对象 114 /// </summary>
115 /// <typeparam name="TEntity">实体类型</typeparam>
116 /// <param name="reader">当前指向的reader</param>
117 /// <returns>实体对象</returns>
118 public static TEntity MapEntity<TEntity>(IDataReader reader) where TEntity : class,new() 119 { 120 try
121 { 122 var props = typeof(TEntity).GetProperties(); 123 var entity = new TEntity(); 124 foreach (var prop in props) 125 { 126 if (prop.CanWrite) 127 { 128 try
129 { 130 var index = reader.GetOrdinal(prop.Name); 131 var data = reader.GetValue(index); 132 if (data != DBNull.Value) 133 { 134 prop.SetValue(entity, Convert.ChangeType(data, prop.PropertyType), null); 135 } 136 } 137 catch (IndexOutOfRangeException) 138 { 139 continue; 140 } 141 } 142 } 143 return entity; 144 } 145 catch
146 { 147 return null; 148 } 149 } 150 #endregion
151 }
在上面代码中,若是程序须要使用具体的链接对象,能够将DBHelper改为一个抽象类,而后把建立链接实例对象改为抽象方法,就能够了。
操作系统
经过上面的简单工厂和用接口来指向操做数据库的各类对象就可以作到简单的跨数据库的功能了