了解DbProviderFactory

  前不久想使用下EF的通用单表增删改的特性,当时使用控制台做测试,怎么弄都没成功,原因出在app.config中,反而在mvc项目中,就没有任何问题。在反复的更改配置的过程中,发现了DbProviderFactory这个类,好奇打开看看其定义。

  1. // 摘要:
  2. // 表示一组方法,这些方法用于创建提供程序对数据源类的实现的实例。
  3. public abstract class DbProviderFactory
  4. {
  5. // 摘要:
  6. // 初始化 System.Data.Common.DbProviderFactory 类的新实例。
  7. protected DbProviderFactory();
  8.  
  9. // 摘要:
  10. // 指定特定的 System.Data.Common.DbProviderFactory 是否支持 System.Data.Common.DbDataSourceEnumerator
  11. // 类。
  12. //
  13. // 返回结果:
  14. // 如果 System.Data.Common.DbProviderFactory 的实例支持 System.Data.Common.DbDataSourceEnumerator
  15. // 类,则为 true;否则为 false。
  16. public virtual bool CanCreateDataSourceEnumerator { get; }
  17.  
  18. // 摘要:
  19. // 返回实现 System.Data.Common.DbCommand 类的提供程序的类的一个新实例。
  20. //
  21. // 返回结果:
  22. // System.Data.Common.DbCommand 的新实例。
  23. public virtual DbCommand CreateCommand();
  24. //
  25. // 摘要:
  26. // 返回实现 System.Data.Common.DbCommandBuilder 类的提供程序的类的一个新实例。
  27. //
  28. // 返回结果:
  29. // System.Data.Common.DbCommandBuilder 的新实例。
  30. public virtual DbCommandBuilder CreateCommandBuilder();
  31. //
  32. // 摘要:
  33. // 返回实现 System.Data.Common.DbConnection 类的提供程序的类的一个新实例。
  34. //
  35. // 返回结果:
  36. // System.Data.Common.DbConnection 的新实例。
  37. public virtual DbConnection CreateConnection();
  38. //
  39. // 摘要:
  40. // 返回实现 System.Data.Common.DbConnectionStringBuilder 类的提供程序的类的一个新实例。
  41. //
  42. // 返回结果:
  43. // System.Data.Common.DbConnectionStringBuilder 的新实例。
  44. public virtual DbConnectionStringBuilder CreateConnectionStringBuilder();
  45. //
  46. // 摘要:
  47. // 返回实现 System.Data.Common.DbDataAdapter 类的提供程序的类的一个新实例。
  48. //
  49. // 返回结果:
  50. // System.Data.Common.DbDataAdapter 的新实例。
  51. public virtual DbDataAdapter CreateDataAdapter();
  52. //
  53. // 摘要:
  54. // 返回实现 System.Data.Common.DbDataSourceEnumerator 类的提供程序的类的一个新实例。
  55. //
  56. // 返回结果:
  57. // System.Data.Common.DbDataSourceEnumerator 的新实例。
  58. public virtual DbDataSourceEnumerator CreateDataSourceEnumerator();
  59. //
  60. // 摘要:
  61. // 返回实现 System.Data.Common.DbParameter 类的提供程序的类的一个新实例。
  62. //
  63. // 返回结果:
  64. // System.Data.Common.DbParameter 的新实例。
  65. public virtual DbParameter CreateParameter();
  66. //
  67. // 摘要:
  68. // 返回提供程序的类的新实例,该实例可实现提供程序的 System.Security.CodeAccessPermission 类的版本。
  69. //
  70. // 参数:
  71. // state:
  72. // System.Security.Permissions.PermissionState 值之一。
  73. //
  74. // 返回结果:
  75. // 指定 System.Security.Permissions.PermissionState 的 System.Security.CodeAccessPermission
  76. // 对象。
  77. public virtual CodeAccessPermission CreatePermission(PermissionState state);
  78. }

  数据库5大对象全了。我想是不是可以通过它来构建出更抽象的数据库访问。后来查询相关资料,并阅读了 PetaPoco 这个微型Orm的源代码,发现其也是使用DbProviderFactory 来创建数据库连接等对象的。

  反观自己在最初学习SQLServer,Oracle,Sqlite时,分别封装了SqlHelp,OracleHelp,SqliteHelp等简化数据库的操作,当时还洋洋自得,现在看来很是无语,代码几乎是一致的,无非是使用的Connection、Command、Adapter、Parameter 等对象的不同,如果使用DbProviderFactory来隔绝具体的实例,是不是可以把这3个数据库Help类封装成一个通用的DbHelp呢?

DbProviderFactory 导入外部的Provider

  初步测试是可以的,不过在通过config文件载入DbProviderFactory时遇到点问题,花费了好一会。过程就不说了,如使用Oracle官网的dll,正确的配置是这样:

<system.data>
<DbProviderFactories>

  1. <add name="OracleClientFactory" invariant="OracleClientFactory" description="Oracle.ManagedDataAccess.Client.OracleClientFactory"
  2. type="Oracle.ManagedDataAccess.Client.OracleClientFactory,Oracle.ManagedDataAccess, Version=4.121.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />

</DbProviderFactories>
</system.data>

  其中要注意的,是 invariant 和 type 属性。

  invariant: 可以通过这个属性值获取到对应DbProviderFactory类型。如,DbProviderFactories.GetFactory("OracleClientFactory") ;

  type:类名(含命名空间),程序集名称,程序集信息。

  我们经常可以在web.config文件中看到类似的type信息,但如果是自己拓展,或者刚刚从Oracle官网上下载的组件呢,该如何得到这些信息。使用Reflector就可以轻松看到,以前一直用Reflector,但从没关注过这一块。

    

    另外通过 DbProviderFactories.GetFactoryClasses(); 可以查看到所有被安装的Provider 哦,通过配置文件导入的也会在其中显示。

  1. <system.data>
  2. <DbProviderFactories>
  3. <add name="SQLiteFactory" invariant="SQLiteFactory"
  4. description="xxxxxxx"
  5. type="System.Data.SQLite.SQLiteFactory,System.Data.SQLite" />
  6. </DbProviderFactories>
  7. </system.data>
  8.  
  9. 检测配置是否成功 var sqlite = DbProviderFactories.GetFactory("SQLiteFactory");

SQLite配置

改善自己的DbHelp类

  数据库操作无非是执行查询与非查询,在与之相关的方法上,封装的没有任何问题。但是在原来的Oracle和Sqlite封装类中,支持分页。如传递普通的查询sql语句会自动生成分页sql呢。其中Oracle 是使用ROWNUM这个来分页,并没有使用ROW_NUMBER()这个分析函数,而Sqlite 则使用limit 关键字,分页更简单。唯独SQLServer,从2000 到 2012,有3种分页方式,这个处理起来有些麻烦。

  在借鉴了Patacoco通过正则拆分SQL,最终拼接出分页的SQL语句的相关代码。具体生成分页语句的思路,则请看 模仿Orm生成分页SQL 。

  测试代码下载

  代码有点长,展开请注意。

  1. public partial class DbHelp
  2. {
  3. enum DBType
  4. {
  5. SqlServer2000,
  6. SqlServer,
  7. Oracle,
  8. SQLite
  9. }
  10.  
  11. #region 成员定义
  12. private DbProviderFactory dbProvider;//数据库Provider
  13. private DBType dbType;//数据库类型
  14. private char pSymbol = '@';//参数符号
  15.  
  16. private DbConnection conn;//连接对象
  17. private string connectionString;//连接字符串
  18. private DbTransaction tran;//事务对象
  19.  
  20. private IList parameterList = new List<DbParameter>();//过程参数列表
  21. private bool hasOutput = false;//是否包含输出参数
  22. private Dictionary<string, object> dicPara = new Dictionary<string, object>();//输出参数列表
  23. #endregion
  24.  
  25. #region 构造方法,实例化连接字符串
  26.  
  27. /// <summary>
  28. /// 读取WebConfig链接字符串
  29. /// </summary>
  30. /// <param name="connectionName">ConnectionString配置名</param>
  31. public DbHelp(string connectionName = "")
  32. {
  33. //默认使用ConnectionString第一项
  34. var config = string.IsNullOrEmpty(connectionName) ?
  35. ConfigurationManager.ConnectionStrings[] :
  36. ConfigurationManager.ConnectionStrings[connectionName];
  37. dbProvider = DbProviderFactories.GetFactory(config.ProviderName);
  38. connectionString = config.ConnectionString;
  39. CommonConstruct(config.ProviderName);
  40. }
  41.  
  42. /// <summary>
  43. /// 有参构造,实例化连接字符串
  44. /// </summary>
  45. /// <param name="provider">DbProvider</param>
  46. /// <param name="connectionString">连接字符串</param>
  47. public DbHelp(DbProviderFactory provider, string connectionString)
  48. {
  49. this.dbProvider = provider;
  50. this.connectionString = connectionString;
  51. CommonConstruct(provider.GetType().Name);
  52. }
  53.  
  54. private void CommonConstruct(string _dbtype = "")
  55. {
  56. // Try using type name first (more reliable)
  57. if (_dbtype.StartsWith("Oracle")) dbType = DBType.Oracle;
  58. else if (_dbtype.StartsWith("SQLite")) dbType = DBType.SQLite;
  59. else if (_dbtype.StartsWith("System.Data.SqlClient")) dbType = DBType.SqlServer;
  60. // else try with provider name
  61. else if (_dbtype.IndexOf("Oracle", StringComparison.InvariantCultureIgnoreCase) >= ) dbType = DBType.Oracle;
  62. else if (_dbtype.IndexOf("SQLite", StringComparison.InvariantCultureIgnoreCase) >= ) dbType = DBType.SQLite;
  63.  
  64. if (dbType == DBType.Oracle)
  65. pSymbol = ':';
  66. else
  67. pSymbol = '@';
  68. }
  69. #endregion
  70.  
  71. #region 实现接口IDisposable
  72. /// <释放资源接口>
  73. /// 实现接口IDisposable
  74. /// </释放资源接口>
  75. public void Dispose()
  76. {
  77. if (conn != null)
  78. {
  79. if (conn.State == ConnectionState.Open)//判断数据库连接池是否打开
  80. {
  81. conn.Close();
  82. }
  83.  
  84. if (parameterList.Count > )//判断参数列表是否清空
  85. {
  86. parameterList.Clear();
  87. }
  88. conn.Dispose();//释放连接池资源
  89. GC.SuppressFinalize(this);//垃圾回收
  90. }
  91. }
  92. #endregion
  93.  
  94. #region 执行SQL或存储过程 并返回影响的行数
  95. /// <summary>
  96. /// 执行SQL,并返回影响的行数
  97. /// </summary>
  98. /// <param name="sql">SQL语句</param>
  99. /// <returns></returns>
  100. public int ExecuteNonQuery(string sql)
  101. {
  102. using (var cmd = CreateCommand(sql))
  103. {
  104. return ExecuteNonQuery(cmd);
  105. }
  106. }
  107.  
  108. /// <summary>
  109. /// 执行存储过程,并返回影响的行数
  110. /// </summary>
  111. /// <param name="storeProcedureName">存储过程名</param>
  112. /// <returns></returns>
  113. public int ExecuteProceudre(string storeProcedureName)
  114. {
  115. using (var cmd = CreateCommand(storeProcedureName, CommandType.StoredProcedure))
  116. {
  117. return ExecuteNonQuery(cmd);
  118. }
  119. }
  120. #endregion
  121.  
  122. #region 执行SQL或者存储过程,并返回DataTable
  123. /// <summary>
  124. /// 执行SQL语句并返回DataTable
  125. /// </summary>
  126. /// <param name="sql">SQL语句</param>
  127. /// <returns></returns>
  128. public DataTable ExecuteSql(string sql)
  129. {
  130. using (var cmd = CreateCommand(sql))
  131. {
  132. return Execute(cmd);
  133. }
  134. }
  135.  
  136. /// <summary>
  137. /// 执行存储过程并返回DataTable
  138. /// </summary>
  139. /// <param name="storeProcedureName">存储过程名</param>
  140. /// <returns></returns>
  141. public DataTable ExecuteProc(string storeProcedureName)
  142. {
  143. using (var cmd = CreateCommand(storeProcedureName, CommandType.StoredProcedure))
  144. {
  145. return Execute(cmd);
  146. }
  147. }
  148. #endregion
  149.  
  150. #region 执行SQL或存储过程并返回DbDataReader
  151. /// <summary>
  152. /// 执行SQL语句并返回DbDataReader
  153. /// </summary>
  154. /// <param name="sql">SQL语句</param>
  155. /// <returns>返回DbDataReader</returns>
  156. public DbDataReader ExecuteReader(string sql)
  157. {
  158. using (var cmd = CreateCommand(sql))
  159. {
  160. return ExecuteReader(cmd);
  161. }
  162. }
  163.  
  164. /// <summary>
  165. /// 执行存储过程并返回DbDataReader
  166. /// </summary>
  167. /// <param name="storeProcedureName">存储过程名</param>
  168. /// <returns>返回DbDataReader</returns>
  169. public DbDataReader ExecuteProcReader(string storeProcedureName)
  170. {
  171. using (var cmd = CreateCommand(storeProcedureName, CommandType.StoredProcedure))
  172. {
  173. return ExecuteReader(cmd);
  174. }
  175. }
  176. #endregion
  177.  
  178. #region 执行统计
  179. /// <summary>
  180. /// 执行SQL语句 返回首行首列的值,一般用于统计
  181. /// </summary>
  182. /// <param name="sql">SQL语句</param>
  183. /// <returns>查询结果首行首列的值转换为整形,转换失败则返回-1</returns>
  184. public int Count(string sql)
  185. {
  186. using (var cmd = CreateCommand(sql))
  187. {
  188. return ExecuteScalar(cmd);
  189. }
  190. }
  191. #endregion
  192.  
  193. #region 测试连接是否成功
  194. /// <summary>
  195. /// 测试连接是否成功
  196. /// </summary>
  197. /// <returns></returns>
  198. public bool HasConnection
  199. {
  200. get
  201. {
  202. try
  203. {
  204. conn = new SqlConnection(connectionString);
  205. conn.Open();
  206. return true;
  207. }
  208. catch
  209. {
  210. return false;
  211. }
  212. finally
  213. {
  214. conn.Close();
  215. }
  216. }
  217. }
  218. #endregion
  219.  
  220. #region 索引器访问
  221. public object this[string name]
  222. {
  223. set
  224. {
  225. this[name, DbType.Object, ParameterDirection.Input] = value;
  226. }
  227. get
  228. {
  229. object obj;
  230. if (dicPara.TryGetValue(name, out obj))
  231. {
  232. return obj;
  233. }
  234. return null;
  235. }
  236. }
  237.  
  238. public object this[string name, DbType dbtype]
  239. {
  240. set
  241. {
  242. this[name, dbtype, ParameterDirection.Input] = value;
  243. }
  244. }
  245.  
  246. public object this[string name, DbType dbType, ParameterDirection direction]
  247. {
  248. set
  249. {
  250. if (name[] != pSymbol) name = pSymbol + name;
  251.  
  252. var para = dbProvider.CreateParameter();
  253. if (dbType != DbType.Object)
  254. para.DbType = dbType;
  255. para.ParameterName = name;
  256. para.Value = value == null ? DBNull.Value : value;
  257. parameterList.Add(para);
  258. }
  259. }
  260. #endregion
  261.  
  262. #region 命令相关处理
  263. /// <summary>
  264. /// 创建DbCommand
  265. /// </summary>
  266. /// <param name="cmdText">命名文本</param>
  267. /// <param name="cmdType">命名类型</param>
  268. /// <returns></returns>
  269. private DbCommand CreateCommand(string cmdText, CommandType cmdType = CommandType.Text)
  270. {
  271. //创建数据库连接对象
  272. if (conn == null || conn.State != ConnectionState.Open)
  273. {
  274. conn = dbProvider.CreateConnection();
  275. conn.ConnectionString = connectionString;
  276. conn.Open();//打开数据库连接池
  277. }
  278.  
  279. //创建Command命令
  280. var cmd = conn.CreateCommand();
  281. cmd.Connection = conn;
  282. cmd.CommandType = cmdType;
  283. if (!string.IsNullOrEmpty(cmdText))
  284. cmd.CommandText = cmdText;
  285. if (tran != null) cmd.Transaction = tran;
  286. cmd.CommandTimeout = ;
  287.  
  288. //加载过程参数
  289. LoadParamter(cmd);
  290. return cmd;
  291. }
  292.  
  293. /// <summary>
  294. /// 创建过程参数
  295. /// </summary>
  296. /// <param name="name">参数名</param>
  297. /// <param name="value">参数值</param>
  298. /// <param name="t">参数值类型</param>
  299. /// <param name="pDirection">参数类型</param>
  300. /// <returns></returns>
  301. private DbParameter CreateParameter(string name, object value, DbType t = DbType.Object, ParameterDirection pDirection = ParameterDirection.Input)
  302. {
  303. var para = dbProvider.CreateParameter();
  304. if (t != DbType.Object) para.DbType = t;
  305. para.Direction = pDirection;
  306. if (name[] == pSymbol)
  307. {
  308. para.ParameterName = name;
  309. }
  310. else
  311. {
  312. para.ParameterName = pSymbol + name;
  313. }
  314. para.Value = value;
  315. return para;
  316. }
  317.  
  318. /// <summary>
  319. /// 执行Command 并返回影响的行数
  320. /// </summary>
  321. /// <param name="cmd">命令</param>
  322. /// <returns></returns>
  323. private int ExecuteNonQuery(DbCommand cmd)
  324. {
  325. try
  326. {
  327. return cmd.ExecuteNonQuery();
  328. }
  329. catch (Exception)
  330. {
  331. conn.Close();
  332. throw;
  333. }
  334. finally
  335. {
  336. if (tran == null) Dispose();
  337. }
  338. }
  339.  
  340. /// <summary>
  341. /// 执行Command 并返回影响的行数
  342. /// </summary>
  343. /// <param name="cmd">命令</param>
  344. /// <returns></returns>
  345. private int ExecuteScalar(DbCommand cmd)
  346. {
  347. try
  348. {
  349. var ret = cmd.ExecuteScalar().ToString();
  350. int i;
  351. if (!int.TryParse(ret, out i))
  352. {
  353. throw new Exception("can't parse it to int,the value is " + ret);
  354. }
  355. return i;
  356. }
  357. catch (Exception)
  358. {
  359. conn.Close();
  360. throw;
  361. }
  362. finally
  363. {
  364. if (tran == null) Dispose();
  365. }
  366. }
  367.  
  368. /// <summary>
  369. /// 执行Command 并返回DataTable
  370. /// </summary>
  371. /// <param name="cmd">命令</param>
  372. /// <returns></returns>
  373. private DataTable Execute(DbCommand cmd)
  374. {
  375. try
  376. {
  377. using (var adapter = dbProvider.CreateDataAdapter())//创建适配器
  378. {
  379. adapter.SelectCommand = cmd;
  380. adapter.SelectCommand.CommandTimeout = ;
  381. var dt = new DataTable();
  382. adapter.Fill(dt);
  383. return dt;//返回结果集
  384. }
  385. }
  386. catch (Exception)
  387. {
  388. conn.Close();
  389. throw;
  390. }
  391. finally
  392. {
  393. if (tran == null) Dispose();
  394. }
  395. }
  396.  
  397. /// <summary>
  398. /// 执行Command 并返回DbDataReader
  399. /// </summary>
  400. /// <param name="cmd">命令</param>
  401. /// <returns></returns>
  402. private DbDataReader ExecuteReader(DbCommand cmd)
  403. {
  404. try
  405. {
  406. return cmd.ExecuteReader(CommandBehavior.CloseConnection);
  407. }
  408. catch (Exception)
  409. {
  410. conn.Close();
  411. throw;
  412. }
  413. finally
  414. {
  415. if (tran == null) Dispose();
  416. }
  417. }
  418.  
  419. /// <summary>
  420. /// 加载输出参数至字典中 仅当执行非查询时才调用
  421. /// </summary>
  422. /// <param name="Parameters"></param>
  423. private void InitDic(DbParameterCollection Parameters)
  424. {
  425. if (hasOutput)
  426. {
  427. dicPara.Clear();
  428. foreach (DbParameter Para in Parameters)
  429. {
  430. if (Para.Direction != ParameterDirection.Input)
  431. {
  432. dicPara.Add(Para.ParameterName, Para.Value);
  433. }
  434. }
  435. hasOutput = false;
  436. }
  437. }
  438.  
  439. /// <summary>
  440. /// 加载过程参数输入至Commond中
  441. /// </summary>
  442. /// <param name="cmd"></param>
  443. private void LoadParamter(DbCommand cmd)
  444. {
  445. if (parameterList.Count != )
  446. {
  447. foreach (DbParameter Para in parameterList)
  448. {
  449. if (!hasOutput && Para.Direction != ParameterDirection.Input)
  450. {
  451. hasOutput = true;
  452. }
  453. cmd.Parameters.Add(Para);
  454. }
  455. parameterList.Clear();
  456. }
  457. }
  458.  
  459. /// <summary>
  460. /// 将参数化Sql转换成纯Sql,便于调试
  461. /// </summary>
  462. /// <param name="strSql">SQL语句</param>
  463. /// <returns></returns>
  464. private string getSqlOnly(DbCommand cmd)
  465. {
  466. var sql = cmd.CommandText;
  467. foreach (DbParameter para in cmd.Parameters)
  468. {
  469. if (para.DbType == DbType.Int16 || para.DbType == DbType.Int32 || para.DbType == DbType.Int64 || para.DbType == DbType.UInt16 || para.DbType == DbType.UInt32 || para.DbType == DbType.UInt64 || para.DbType == DbType.Decimal || para.DbType == DbType.Double || para.DbType == DbType.Single)
  470. {
  471. sql = sql.Replace(para.ParameterName, para.Value.ToString());
  472. }
  473. else if (pSymbol == '@' || para.DbType == DbType.AnsiString || para.DbType == DbType.String || para.DbType == DbType.StringFixedLength)
  474. {
  475. sql = sql.Replace(para.ParameterName, "'" + para.Value.ToString() + "'");
  476. }
  477. else if (pSymbol == ':' || para.DbType == DbType.DateTime || para.DbType == DbType.DateTime2 || para.DbType == DbType.DateTimeOffset)
  478. {
  479. //排除未知的时间类型
  480. DateTime time;
  481. if (DateTime.TryParse(para.Value.ToString(), out time))
  482. {
  483. sql = sql.Replace(para.ParameterName, "to_date('" + time.ToString() + "','yyyy-MM-dd hh24:mi:ss')");
  484. }
  485. }
  486. }
  487. return sql;
  488. }
  489. #endregion
  490.  
  491. #region 分页查询
  492. /// <summary>
  493. /// 对指定Sql语句查询的结果集进行分页
  494. /// </summary>
  495. /// <param name="sql">sql语句</param>
  496. /// <param name="start">结果集起始行号,不包括此行</param>
  497. /// <param name="limit">取出的行数</param>
  498. /// <returns></returns>
  499. public DataTable ExecuteSql(string sql, int start, int limit)
  500. {
  501. string[] sqls;
  502. var pageParms = CreatePageSql(sql, out sqls, start, limit);
  503. using (var cmd = CreateCommand(sqls[]))
  504. {
  505. cmd.Parameters.AddRange(pageParms);
  506. return Execute(cmd);
  507. }
  508. }
  509.  
  510. /// <summary>
  511. /// 对指定Sql语句查询的结果集进行分页
  512. /// </summary>
  513. /// <param name="sql">sql语句</param>
  514. /// <param name="start">结果集起始行号,不包括此行</param>
  515. /// <param name="limit">取出的行数</param>
  516. /// <returns></returns>
  517. public DbDataReader ExecuteReader(string sql, int start, int limit)
  518. {
  519. string[] sqls;
  520. var pageParms = CreatePageSql(sql, out sqls, start, limit);
  521. using (var cmd = CreateCommand(sqls[]))
  522. {
  523. cmd.Parameters.AddRange(pageParms);
  524. return ExecuteReader(cmd);
  525. }
  526. }
  527.  
  528. /// <summary>
  529. /// 对指定Sql语句查询的结果集进行分页
  530. /// </summary>
  531. /// <param name="sql">sql语句</param>
  532. /// <param name="start">结果集起始行号,不包括此行</param>
  533. /// <param name="limit">取出的行数</param>
  534. /// <param name="count">输出总行数</param>
  535. /// <returns></returns>
  536. public DataTable ExecuteSql(string sql, int start, int limit, out int count)
  537. {
  538. //查看是否已经使用事务,若没有使用事务,这里必须使用事务执行
  539. var alreadyUserTran = tran != null;
  540. if (!alreadyUserTran)
  541. BeginTransation();
  542.  
  543. string[] sqls;
  544. var pageParms = CreatePageSql(sql, out sqls, start, limit, true);
  545. using (var cmd = CreateCommand(sqls[]))
  546. {
  547. count = ExecuteScalar(cmd);
  548.  
  549. //加载逆序分页 并返回过程参数
  550. var pageReverse = CreatePageSqlReverse(sql, ref sqls, start, limit, count);
  551. if (pageReverse != null)
  552. cmd.Parameters.AddRange(pageParms);
  553. else
  554. cmd.Parameters.AddRange(pageParms);
  555.  
  556. cmd.CommandText = sqls[];
  557. var dt = Execute(cmd);
  558.  
  559. //如果事先已开启事务,则不在此处提交事务,应由用户调用时手动提交,否则自动提交方法
  560. if (!alreadyUserTran)
  561. tran.Commit();
  562.  
  563. return dt;
  564. }
  565. }
  566.  
  567. /// <summary>
  568. /// 对指定Sql语句查询的结果集进行分页
  569. /// </summary>
  570. /// <param name="sql">sql语句</param>
  571. /// <param name="start">结果集起始行号,不包括此行</param>
  572. /// <param name="limit">取出的行数</param>
  573. /// <param name="count">输出总行数</param>
  574. /// <returns></returns>
  575. public DbDataReader ExecuteReader(string sql, int start, int limit, out int count)
  576. {
  577. //查看是否已经使用事务,若没有使用事务,这里必须使用事务执行
  578. var alreadyUserTran = tran != null;
  579. if (!alreadyUserTran)
  580. BeginTransation();
  581.  
  582. string[] sqls;
  583. var pageParms = CreatePageSql(sql, out sqls, start, limit, true);
  584. using (var cmd = CreateCommand(sqls[]))
  585. {
  586. count = ExecuteScalar(cmd);
  587.  
  588. //加载逆序分页 并返回过程参数
  589. var pageReverse = CreatePageSqlReverse(sql, ref sqls, start, limit, count);
  590. if (pageReverse != null)
  591. cmd.Parameters.AddRange(pageParms);
  592. else
  593. cmd.Parameters.AddRange(pageParms);
  594.  
  595. cmd.CommandText = sqls[];
  596. return ExecuteReader(cmd);
  597. }
  598. }
  599. #endregion
  600.  
  601. #region 开启事务
  602. /// <summary>
  603. /// 开启事务
  604. /// </summary>
  605. /// <returns></returns>
  606. public void BeginTransation()
  607. {
  608. //创建数据库连接对象
  609. if (conn == null || conn.State != ConnectionState.Open)
  610. {
  611. conn = dbProvider.CreateConnection();
  612. conn.ConnectionString = connectionString;
  613. conn.Open();//打开数据库连接池
  614. }
  615. tran = conn.BeginTransaction();
  616. }
  617.  
  618. /// <summary>
  619. /// 提交事务
  620. /// </summary>
  621. public void Commit()
  622. {
  623. if (tran != null)
  624. {
  625. tran.Commit();
  626. tran.Dispose();
  627. tran = null;
  628. Dispose();
  629. }
  630. }
  631.  
  632. /// <summary>
  633. /// 回滚事务
  634. /// </summary>
  635. public void Rollback()
  636. {
  637. if (tran != null)
  638. {
  639. tran.Rollback();
  640. tran.Dispose();
  641. tran = null;
  642. Dispose();
  643. }
  644. }
  645. #endregion
  646.  
  647. #region 生成 分页SQL语句
  648. /// <summary>
  649. /// 匹配移除Select后的sql
  650. /// </summary>
  651. private Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
  652. /// <summary>
  653. /// 匹配SQL语句中Order By字段
  654. /// </summary>
  655. private Regex rxOrderBy = new Regex(@"\b(?<ordersql>ORDER\s+BY\s+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+)(?:\s+(?<order>ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
  656. /// <summary>
  657. /// 匹配SQL语句中Distinct
  658. /// </summary>
  659. private Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
  660. /// <summary>
  661. /// 分析Sql语句 输出分析数组 信息依次为:
  662. /// 0.countsql
  663. /// 1.pageSql(保留位置此处不做分析)
  664. /// 2.移除了select的sql
  665. /// 3.order by 字段 desc
  666. /// 4.order by 字段
  667. /// 5.desc
  668. /// </summary>
  669. /// <param name="sql"></param>
  670. /// <returns></returns>
  671. private string[] SplitSqlForPaging(string sql)
  672. {
  673. var sqlInfo = new string[];
  674. // Extract the columns from "SELECT <whatever> FROM"
  675. var m = rxColumns.Match(sql);
  676. if (!m.Success)
  677. return null;
  678.  
  679. // Save column list and replace with COUNT(*)
  680. Group g = m.Groups[];
  681. sqlInfo[] = sql.Substring(g.Index);
  682.  
  683. if (rxDistinct.IsMatch(sqlInfo[]))
  684. sqlInfo[] = sql.Substring(, g.Index) + "COUNT(" + m.Groups[].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
  685. else
  686. sqlInfo[] = sql.Substring(, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
  687.  
  688. // Look for an "ORDER BY <whatever>" clause
  689. m = rxOrderBy.Match(sqlInfo[]);
  690. if (!m.Success)
  691. {
  692. sqlInfo[] = null;
  693. }
  694. else
  695. {
  696. g = m.Groups[];
  697. sqlInfo[] = g.ToString();
  698. //统计的SQL 移除order
  699. sqlInfo[] = sqlInfo[].Substring(, g.Index) + sqlInfo[].Substring(g.Index + g.Length);
  700. //存储排序信息
  701. sqlInfo[] = m.Groups["ordersql"].Value;//order by xxx
  702. sqlInfo[] = m.Groups["order"].Value;//desc
  703.  
  704. //select部分 移除order
  705. sqlInfo[] = sqlInfo[].Replace(sqlInfo[], string.Empty);
  706. }
  707.  
  708. return sqlInfo;
  709. }
  710.  
  711. /// <summary>
  712. /// 生成逆序分页Sql语句
  713. /// </summary>
  714. /// <param name="sql"></param>
  715. /// <param name="sqls"></param>
  716. /// <param name="start"></param>
  717. /// <param name="limit"></param>
  718. /// <param name="total"></param>
  719. private DbParameter[] CreatePageSqlReverse(string sql, ref string[] sqls, int start, int limit, int total = )
  720. {
  721. //如果总行数不多或分页的条数位于前半部分,没必要逆序分页
  722. if (total < || start <= total / )
  723. {
  724. return null;
  725. }
  726.  
  727. //sql正则分析过后的数组有5个值,若未分析,此处分析
  728. if (sqls == null || sqls.Length == )
  729. {
  730. sqls = SplitSqlForPaging(sql);
  731. if (sqls == null)
  732. {
  733. //无法解析的SQL语句
  734. throw new Exception("can't parse sql to pagesql ,the sql is " + sql);
  735. }
  736. }
  737.  
  738. //如果未定义排序规则,则无需做逆序分页计算
  739. if (string.IsNullOrEmpty(sqls[]))
  740. {
  741. return null;
  742. }
  743.  
  744. //逆序分页检查
  745. string sqlOrder = sqls[];
  746. int end = start + limit;
  747.  
  748. //获取逆序排序的sql
  749. string sqlOrderChange = string.Compare(sqls[], "desc", true) == ?
  750. string.Format("{0} ASC ", sqls[]) :
  751. string.Format("{0} DESC ", sqls[]);
  752.  
  753. /*理论
  754. * total:10000 start:9980 limit:10
  755. * 则 end:9990 分页条件为 RN >= 9980+1 and RN <= 9990
  756. * 逆序调整后
  757. * start = total - start = 20
  758. * end = total - end + 1 = 11
  759. * 交换start和end,分页条件为 RN >= 11 and RN<= 20
  760. */
  761. //重新计算start和end
  762. start = total - start;
  763. end = total - end + ;
  764. //交换start end
  765. start = start + end;
  766. end = start - end;
  767. start = start - end;
  768.  
  769. //定义分页SQL
  770. var pageSql = new StringBuilder();
  771.  
  772. if (dbType == DBType.SqlServer2000)
  773. {
  774. pageSql.AppendFormat("SELECT TOP @PageLimit * FROM ( SELECT TOP @PageEnd {0} {1} ) ", sqls[], sqlOrderChange);
  775. }
  776. else if (dbType == DBType.SqlServer)
  777. {
  778. //组织分页SQL语句
  779. pageSql.AppendFormat("SELECT PageTab.* FROM ( SELECT TOP @PageEnd ROW_NUMBER() over ({0}) RN , {1} ) PageTab ",
  780. sqlOrderChange,
  781. sqls[]);
  782.  
  783. //如果查询不是第一页,则需要判断起始行号
  784. if (start > )
  785. {
  786. pageSql.Append("Where RN >= :PageStart ");
  787. }
  788. }
  789. else if (dbType == DBType.Oracle)
  790. {
  791. pageSql.AppendFormat("SELECT ROWNUM RN, PageTab.* FROM ( Select {0} {1} ) PageTab where ROWNUM <= :PageEnd ", sqls[], sqlOrderChange);
  792.  
  793. //如果查询不是第一页,则需要判断起始行号
  794. if (start > )
  795. {
  796. pageSql.Insert(, "SELECT * FROM ( ");
  797. pageSql.Append(" ) ");
  798. pageSql.Append(" WHERE RN>= :PageStart ");
  799. }
  800. }
  801. else if (dbType == DBType.SQLite)
  802. {
  803. pageSql.AppendFormat("SELECT * FROM ( SELECT {0} {1} limit @PageStart,@PageLimit ) PageTab ", sqls[], sqlOrderChange);
  804. }
  805.  
  806. //恢复排序
  807. pageSql.Append(sqlOrder);
  808.  
  809. //存储生成的分页SQL语句
  810. sqls[] = pageSql.ToString();
  811.  
  812. //临时测试
  813. //sqls[1] = sqls[1].Replace("@", "").Replace(":", "").Replace("PageStart", start + "").Replace("PageEnd", end + "").Replace("PageLimit", limit + "");
  814.  
  815. //组织过程参数
  816. DbParameter[] paras = null;
  817. if (dbType == DBType.SqlServer2000 || dbType == DBType.SQLite)
  818. {
  819. paras = new DbParameter[];
  820. paras[] = CreateParameter("@PageLimit", limit);
  821. paras[] = CreateParameter("@PageEnd", end);
  822. }
  823. else if (start > )
  824. {
  825. paras = new DbParameter[];
  826. paras[] = CreateParameter("@PageStart", start);
  827. paras[] = CreateParameter("@PageEnd", end);
  828. }
  829. else
  830. {
  831. paras = new DbParameter[] { CreateParameter("@PageEnd", end) };
  832. }
  833.  
  834. return paras;
  835. }
  836.  
  837. /// <summary>
  838. /// 生成常规Sql语句
  839. /// </summary>
  840. /// <param name="sql"></param>
  841. /// <param name="sqls"></param>
  842. /// <param name="start"></param>
  843. /// <param name="limit"></param>
  844. /// <param name="createCount"></param>
  845. private DbParameter[] CreatePageSql(string sql, out string[] sqls, int start, int limit, bool createCount = false)
  846. {
  847. //需要输出的sql数组
  848. sqls = null;
  849.  
  850. //生成count的SQL语句 SqlServer生成分页,必须通过正则拆分
  851. if (createCount || dbType == DBType.SqlServer || dbType == DBType.SqlServer2000)
  852. {
  853. sqls = SplitSqlForPaging(sql);
  854. if (sqls == null)
  855. {
  856. //无法解析的SQL语句
  857. throw new Exception("can't parse sql to pagesql ,the sql is " + sql);
  858. }
  859. }
  860. else
  861. {
  862. sqls = new string[];
  863. }
  864.  
  865. //组织分页SQL语句
  866. var pageSql = new StringBuilder();
  867.  
  868. //构建分页参数
  869. var end = start + limit;
  870. start++;
  871.  
  872. if (dbType == DBType.SqlServer2000)
  873. {
  874. pageSql.AppendFormat("SELECT TOP @PageEnd {0} {1}", sqls[], sqls[]);
  875.  
  876. if (start > )
  877. {
  878. var orderChange = string.IsNullOrEmpty(sqls[]) ? null :
  879. string.Compare(sqls[], "desc", true) == ?
  880. string.Format("{0} ASC ", sqls[]) :
  881. string.Format("{0} DESC ", sqls[]);
  882. pageSql.Insert(, "SELECT TOP 100 PERCENT * FROM (SELECT TOP @PageLimit * FROM ( ");
  883. pageSql.AppendFormat(" ) PageTab {0} ) PageTab2 {1}", orderChange, sqls[]);
  884. }
  885. }
  886. else if (dbType == DBType.SqlServer)
  887. {
  888. pageSql.AppendFormat(" Select top (@PageEnd) ROW_NUMBER() over ({0}) RN , {1}",
  889. string.IsNullOrEmpty(sqls[]) ? "ORDER BY (SELECT NULL)" : sqls[],
  890. sqls[]);
  891.  
  892. //如果查询不是第一页,则需要判断起始行号
  893. if (start > )
  894. {
  895. pageSql.Insert(, "Select PageTab.* from ( ");
  896. pageSql.Append(" ) PageTab Where RN >= @PageStart");
  897. }
  898. }
  899. else if (dbType == DBType.Oracle)
  900. {
  901. pageSql.Append("select ROWNUM RN, PageTab.* from ");
  902. pageSql.AppendFormat(" ( {0} ) PageTab ", sql);
  903. pageSql.Append(" where ROWNUM <= :PageEnd ");
  904.  
  905. //如果查询不是第一页,则需要判断起始行号
  906. if (start > )
  907. {
  908. pageSql.Insert(, "select * from ( ");
  909. pageSql.Append(" ) Where RN>= :PageStart ");
  910. }
  911. }
  912. else if (dbType == DBType.SQLite)
  913. {
  914. pageSql.AppendFormat("{0} limit @PageStart,@PageLimit", sql, start, limit);
  915. }
  916.  
  917. //存储生成的分页SQL语句
  918. sqls[] = pageSql.ToString();
  919.  
  920. //临时测试
  921. //sqls[1] = sqls[1].Replace("@", "").Replace(":", "").Replace("PageStart", start + "").Replace("PageEnd", end + "").Replace("PageLimit", limit + "");
  922.  
  923. //组织过程参数
  924. DbParameter[] paras;
  925. if (dbType == DBType.SqlServer2000 || dbType == DBType.SQLite)
  926. {
  927. paras = new DbParameter[];
  928. paras[] = CreateParameter("@PageLimit", limit);
  929. paras[] = CreateParameter("@PageEnd", end);
  930. }
  931. else if (start > )
  932. {
  933. paras = new DbParameter[];
  934. paras[] = CreateParameter("@PageStart", start);
  935. paras[] = CreateParameter("@PageEnd", end);
  936. }
  937. else
  938. {
  939. paras = new DbParameter[] { CreateParameter("@PageEnd", end) };
  940. }
  941.  
  942. return paras;
  943. }
  944. #endregion
  945.  
  946. }

DbHelp

要知道的DbProviderFactory的更多相关文章

  1. 程序员必须要知道的Hadoop的一些事实

    程序员必须要知道的Hadoop的一些事实.现如今,Apache Hadoop已经无人不知无人不晓.当年雅虎搜索工程师Doug Cutting开发出这个用以创建分布式计算机环境的开源软...... 1: ...

  2. 【转载】在IT界取得成功应该知道的10件事

     在IT界取得成功应该知道的10件事 2011-08-11 13:31:30 分类: 项目管理 导读:前面大多数文章都是Jack Wallen写的,这是他的新作,看来要成为NB程序员还要不停的自我总结 ...

  3. 理工科应该的知道的C/C++数学计算库(转)

    理工科应该的知道的C/C++数学计算库(转) 作为理工科学生,想必有限元分析.数值计算.三维建模.信号处理.性能分析.仿真分析...这些或多或少与我们常用的软件息息相关,假如有一天你只需要这些大型软件 ...

  4. 你应该知道的10个奇特的 HTML5 单页网站

    网页设计师努力寻找新的方式来展现内容.其中一个大的趋势是单页网站,现在被世界上的一些大的品牌广泛采用,使用它们来为用户提供一个快速,干净和简单的而且​​美丽的网站. 下面是10个令人惊叹的单页 H​​ ...

  5. Git / 程序员需要知道的12个Git高级命令

    众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...

  6. 你应该知道的RPC原理

    你应该知道的RPC原理 在学校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服务消费方和服务提供方是本地调用关系. 而一旦踏入公司尤其是大型互 ...

  7. 希望早几年知道的5个Unix命令

    原文: http://spin.atomicobject.com/2013/09/09/5-unix-commands/ 希望早几年知道的5个Unix命令 使用*nix系统已经有一段时间了.但是还是有 ...

  8. 关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造)

    关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造) 摘要:在中文搜索中的标点.符号往往也是有语义的,比如我们要搜索“C++”或是“C#”,我们不希望搜索出来的全是“C”吧?那样对程 ...

  9. 对于JavaScript的函数.NET开发人员应该知道的11件事

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 昨天小感冒今天重感冒,也不能长篇大论.如果你是.NET开发人员,在进入前端开发领域的时候,对 ...

随机推荐

  1. 学习Pytbon第十天 函数2 内置方法和匿名函数

    print( all([1,-5,3]) )#如果可迭代对象里所有元素都为真则返回真.0不为真print( any([1,2]) )#如果数据里面任意一个数据为真返回则为真a= ascii([1,2, ...

  2. Pandas 数值计算和统计基础

    1.(1) # 基本参数:axis.skipna import numpy as np import pandas as pd df = pd.DataFrame({'key1':[4,5,3,np. ...

  3. idea中用maven打包spring的java项目(非web)

    之前一直用安装的maven打包spring的javaweb项目,用的是mvn assembly:assembly打包,这次打包非web的spring项目,遇到许多问题,特记录一下正确步骤. 1.配置p ...

  4. 1 HTML + CSS

    1.HTML的基础用法 2.标签的嵌套 3.常见的网页结构 header content footer

  5. nodejs基础1

    nodejs学习网站: https://github.com/alsotang/node-lessons 1.全局对象 (1)node中没有window对象,有global对象替代window对象 g ...

  6. 常用模块(time)

    import time # data = time.time() # 获取时间戳# data = time.localtime() # 获取操作系统时间,也称本地时间,可传入时间戳# data = t ...

  7. postman导出excel出现response

    https://jingyan.baidu.com/article/915fc414559b4351394b2084.html

  8. 孤荷凌寒自学python第六十五天学习mongoDB的基本操作并进行简单封装4

    孤荷凌寒自学python第六十五天学习mongoDB的基本操作并进行简单封装4 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第十一天. 今天继续学习mongoDB的简单操作 ...

  9. 孤荷凌寒自学python第十三天python代码的外部模块引用与基本赋值语句

    孤荷凌寒自学python第十三天python代码的外部模块引用与基本赋值语句 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 从结构化编程流行以来,代码便被分块存储,称之为模块或库. 在pyt ...

  10. Vue2 全局过滤器(vue-cli)

    先看官方简介: 当前组件注册: export default { data () { return {} }, filters:{ orderBy (){ // doSomething }, uppe ...