利用ADO.NET的体系架构打造通用的数据库访问通用类

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介:

说明
在之前周公曾写过针对不同数据库的数据库访问通用类,如针对SQLite的、针对Access的、针对Oracle的、针对SQL Server的。总结了这些通用类的通用方法,其实无非就是针对不同类型的数据库创建Connection、Command、DataAdapter及DataReader,然后对外提供范围ExecuteTable(),ExecuteDataReader、ExecuteScalar()及ExecuteNonQuery()方法,有了这四个方法我们就可以完成针对数据库的所有操作了。在之前周公就曾经想过将这些数据库访问通用类提炼出来,写成一个针对各种数据库通用的数据库通用类,按照一般的方法写的话就需要收集常用的数据库访问类库才能编译,如果用反射的办法虽然也可以解决问题,但是周公又不愿意代码里到处都是反射的代码,在针对.NET Framework进行分析的基础上,写成了今天的数据库访问通用类。
分析
请先看下图:
 

 

在System.Data.Common命名空间下定义了针对所有数据库的Connection、Command、DataAdapter及DataReader对象的抽象类,分别是DbConnection、DbCommand、DbDataAdapter及DbDataReader,在这些抽象类中定义了针对所有数据库的通用方法和属性,不光是在.NET Framework中微软提供的针对ODBC、OleDB、Oracle、SQL Server类中如此,在微软未提供、由数据库厂商提供的ADO.NET类也是如此(假如有一天你自己也开发了一个数据库,为了提供给.NET开发人员使用,也应该遵循这个规定)。除此之外,在System.Data.Common命名空间下还提供了两个类,一个是DbProviderFactories,另一个是DbProviderFactory,DbProviderFactories类提供的方法有两个(包括一个重载形式),GetFactoryClasses()方法返回在系统中注册的DbProviderFactory类(如果在系统中注册了,就会在machine.config中的<configuration><system.data><DbProviderFactories>下添加针对这个数据库的相关信息),GetFactory()的两个重载方法都是返回一个指定的DbProviderFactor抽象类,在DbProviderFactory抽象类中又定义了创建DbConnection、DbCommand、DbDataAdapter及DbDataReader的方法。而不同的数据库访问类程序集中又都提供了对DbProviderFactory这个抽象类的实现(包括所有由数据库厂商提供的ADO.NET类)。所以我们要解决的问题是如何创建针对不同数据库的DbProviderFactory这个抽象类的实现。
解决
我们知道machine.config是所有config文件的鼻祖,包括web.config和app.config,程序在获取配置信息时会首先从距离自己层次关系最近的config文件查询起,一直到machine.config文件为止。那么我们就首先从自己的config文件做章。
下面的一段代码是从machine.config文件中摘取出来的:
 

 
  1. <system.data> 
  2.     <DbProviderFactories> 
  3.         <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
  4.         <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
  5.         <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
  6.         <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
  7.         <add name="Microsoft SQL Server Compact Data Provider" invariant="System.Data.SqlServerCe.3.5" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=3.5.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" /> 
  8.     </DbProviderFactories> 
  9. </system.data> 


我们可以看到每个节点的组成很固定,都有name、invariant、description、type四个属性,它们的作用分别如下:
name:数据提供程序的可识别名称。
invariant:可以以编程方式用于引用数据提供程序的名称。
description:数据提供程序的可识别描述。
type:工厂类的完全限定名,它包含用于实例化该对象的足够的信息。
注意在全局范围类不同存在相同的invariant值,比如说在machine.config中已经定义了,就不能在自己的config文件中重复定义。此外,在type中Version、Culture及PublicKeyToken也不是必须的。
刚刚已经说了程序在获取配置信息时会首先从距离自己层次关系最近的config文件查询起,一直到machine.config文件为止,那么我们可以在自己的config文件中定义非微软提供的数据库访问类库信息,在这里周公也提供了绝大部分非微软提供的数据库访问类库信息,如下:
 

 
  1. <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /> 
  2. <add name="Informix Data Provider" invariant="IBM.Data.Informix" description=".Net Framework Data Provider for Informix" type="IBM.Data.Informix.IfxFactory, IBM.Data.Informix" /> 
  3. <add name="DB2 Data Provider" invariant="IBM.Data.DB2.iSeries" description=".Net Framework Data Provider for DB2 iSeries" type="IBM.Data.DB2.iSeries.DB2Factory, IBM.Data.DB2.iSeries" /> 
  4. <add name="Firebird Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description="Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient"/> 
  5. <add name="Oracle Data Provider" invariant="Oracle.DataAccess.Client" description=".Net Framework Data Provider for Oracle" type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess" /> 
  6. <add name="PostgreSQL Data Provider Data Provider" invariant="Npgsql" description=".Net Framework Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, System.Data" /> 


当然,也并不是在每次开发的时候都需要添加这些信息,如果本机安装了对应的程序集,那么就无需配置,除此之外,对于根本不会访问的数据库类型也不必添加对应的程序集信息。
代码实现
 

 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Data;  
  5. using System.Data.Common;  
  6. using System.Reflection;  
  7. using System.Text.RegularExpressions;  
  8.  
  9. /// <summary>  
  10. /// 通用数据库访问类,封装了对数据库的常见操作  
  11. /// 作者:周公  
  12. /// 日期:2011-07-18  
  13. /// 博客地址:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com  
  14. /// 说明:(1)任何人都可以免费使用,请尽量保持此段说明。  
  15. ///      (2)这个版本还不是最终版本,有任何意见或建议请到http://weibo.com/zhoufoxcn处留言。  
  16. /// </summary>  
  17. public sealed class DbUtility  
  18. {  
  19.  public string ConnectionString { getprivate set; }  
  20.  private DbProviderFactory providerFactory;  
  21.  /// <summary>  
  22.  /// 构造函数  
  23.  /// </summary>  
  24.  /// <param name="connectionString">数据库连接字符串</param>  
  25.  /// <param name="providerType">数据库类型枚举,参见<paramref name="providerType"/></param>  
  26.  public DbUtility(string connectionString,DbProviderType providerType)  
  27.  {  
  28.   ConnectionString = connectionString;  
  29.   providerFactory = ProviderFactory.GetDbProviderFactory(providerType);  
  30.   if (providerFactory == null)  
  31.   {  
  32.    throw new ArgumentException("Can't load DbProviderFactory for given value of providerType");  
  33.   }  
  34.  }  
  35.  /// <summary>     
  36.  /// 对数据库执行增删改操作,返回受影响的行数。     
  37.  /// </summary>     
  38.  /// <param name="sql">要执行的增删改的SQL语句</param>     
  39.  /// <param name="parameters">执行增删改语句所需要的参数</param>  
  40.  /// <returns></returns>    
  41.  public int ExecuteNonQuery(string sql,IList<DbParameter> parameters)  
  42.  {  
  43.   return ExecuteNonQuery(sql, parameters, CommandType.Text);  
  44.  }  
  45.  /// <summary>     
  46.  /// 对数据库执行增删改操作,返回受影响的行数。     
  47.  /// </summary>     
  48.  /// <param name="sql">要执行的增删改的SQL语句</param>     
  49.  /// <param name="parameters">执行增删改语句所需要的参数</param>  
  50.  /// <param name="commandType">执行的SQL语句的类型</param>  
  51.  /// <returns></returns>  
  52.  public int ExecuteNonQuery(string sql,IList<DbParameter> parameters, CommandType commandType)  
  53.  {  
  54.   using (DbCommand command = CreateDbCommand(sql, parameters, commandType))  
  55.   {  
  56.    command.Connection.Open();  
  57.    int affectedRows=command.ExecuteNonQuery();  
  58.    command.Connection.Close();  
  59.    return affectedRows;  
  60.   }  
  61.  }  
  62.  /// <summary>     
  63.  /// 执行一个查询语句,返回一个关联的DataReader实例     
  64.  /// </summary>     
  65.  /// <param name="sql">要执行的查询语句</param>     
  66.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>  
  67.  /// <returns></returns>   
  68.  public DbDataReader ExecuteReader(string sql, IList<DbParameter> parameters)  
  69.  {  
  70.   return ExecuteReader(sql, parameters, CommandType.Text);  
  71.  }  
  72.  /// <summary>     
  73.  /// 执行一个查询语句,返回一个关联的DataReader实例     
  74.  /// </summary>     
  75.  /// <param name="sql">要执行的查询语句</param>     
  76.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>  
  77.  /// <param name="commandType">执行的SQL语句的类型</param>  
  78.  /// <returns></returns>   
  79.  public DbDataReader ExecuteReader(string sql, IList<DbParameter> parameters, CommandType commandType)  
  80.  {  
  81.   DbCommand command = CreateDbCommand(sql, parameters, commandType);  
  82.   command.Connection.Open();  
  83.   return command.ExecuteReader(CommandBehavior.CloseConnection);  
  84.  }  
  85.  /// <summary>     
  86.  /// 执行一个查询语句,返回一个包含查询结果的DataTable     
  87.  /// </summary>     
  88.  /// <param name="sql">要执行的查询语句</param>     
  89.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>  
  90.  /// <returns></returns>  
  91.  public DataTable ExecuteDataTable(string sql, IList<DbParameter> parameters)  
  92.  {  
  93.   return ExecuteDataTable(sql, parameters, CommandType.Text);  
  94.  }  
  95.  /// <summary>     
  96.  /// 执行一个查询语句,返回一个包含查询结果的DataTable     
  97.  /// </summary>     
  98.  /// <param name="sql">要执行的查询语句</param>     
  99.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>  
  100.  /// <param name="commandType">执行的SQL语句的类型</param>  
  101.  /// <returns></returns>  
  102.  public DataTable ExecuteDataTable(string sql, IList<DbParameter> parameters, CommandType commandType)  
  103.  {  
  104.   using (DbCommand command = CreateDbCommand(sql, parameters, commandType))  
  105.   {  
  106.    using (DbDataAdapter adapter = providerFactory.CreateDataAdapter())  
  107.    {  
  108.     adapter.SelectCommand = command;  
  109.     DataTable data = new DataTable();  
  110.     adapter.Fill(data);  
  111.     return data;  
  112.    }  
  113.   }  
  114.  }  
  115.  /// <summary>     
  116.  /// 执行一个查询语句,返回查询结果的第一行第一列     
  117.  /// </summary>     
  118.  /// <param name="sql">要执行的查询语句</param>     
  119.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>     
  120.  /// <returns></returns>     
  121.  public Object ExecuteScalar(string sql, IList<DbParameter> parameters)  
  122.  {  
  123.   return ExecuteScalar(sql, parameters, CommandType.Text);  
  124.  }  
  125.  
  126.  /// <summary>     
  127.  /// 执行一个查询语句,返回查询结果的第一行第一列     
  128.  /// </summary>     
  129.  /// <param name="sql">要执行的查询语句</param>     
  130.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>     
  131.  /// <param name="commandType">执行的SQL语句的类型</param>  
  132.  /// <returns></returns>     
  133.  public Object ExecuteScalar(string sql, IList<DbParameter> parameters,CommandType commandType)  
  134.  {  
  135.   using (DbCommand command = CreateDbCommand(sql, parameters, commandType))  
  136.   {  
  137.    command.Connection.Open();  
  138.    object result = command.ExecuteScalar();  
  139.    command.Connection.Close();  
  140.    return result;  
  141.   }  
  142.  }  
  143.  /// <summary>  
  144.  /// 创建一个DbCommand对象  
  145.  /// </summary>  
  146.  /// <param name="sql">要执行的查询语句</param>     
  147.  /// <param name="parameters">执行SQL查询语句所需要的参数</param>  
  148.  /// <param name="commandType">执行的SQL语句的类型</param>  
  149.  /// <returns></returns>  
  150.  private DbCommand CreateDbCommand(string sql, IList<DbParameter> parameters, CommandType commandType)  
  151.  {  
  152.   DbConnection connection=providerFactory.CreateConnection();  
  153.   DbCommand command = providerFactory.CreateCommand();  
  154.   connection.ConnectionString = ConnectionString;  
  155.   command.CommandText = sql;  
  156.   command.CommandType = commandType;  
  157.   command.Connection = connection;  
  158.   if (!(parameters == null || parameters.Count == 0))  
  159.   {  
  160.    foreach (DbParameter parameter in parameters)  
  161.    {  
  162.     command.Parameters.Add(parameter);  
  163.    }  
  164.   }  
  165.   return command;  
  166.  }  
  167. }  
  168. /// <summary>  
  169. /// 数据库类型枚举  
  170. /// </summary>  
  171. public enum DbProviderType:byte 
  172. {  
  173.  SqlServer,  
  174.  MySql,  
  175.  SQLite,  
  176.  Oracle,  
  177.  ODBC,  
  178.  OleDb,  
  179.  Firebird,  
  180.  PostgreSql,  
  181.  DB2,  
  182.  Informix,  
  183.  SqlServerCe  
  184. }  
  185. /// <summary>  
  186. /// DbProviderFactory工厂类  
  187. /// </summary>  
  188. public class ProviderFactory  
  189. {  
  190.  private static Dictionary<DbProviderType, string> providerInvariantNames = new Dictionary<DbProviderType, string>();  
  191.  private static Dictionary<DbProviderType, DbProviderFactory> providerFactoies = new Dictionary<DbProviderType, DbProviderFactory>(20);  
  192.  static ProviderFactory()  
  193.  {  
  194.   //加载已知的数据库访问类的程序集  
  195.   providerInvariantNames.Add(DbProviderType.SqlServer, "System.Data.SqlClient");  
  196.   providerInvariantNames.Add(DbProviderType.OleDb, "System.Data.OleDb");  
  197.   providerInvariantNames.Add(DbProviderType.ODBC, "System.Data.ODBC");  
  198.   providerInvariantNames.Add(DbProviderType.Oracle, "Oracle.DataAccess.Client");  
  199.   providerInvariantNames.Add(DbProviderType.MySql, "MySql.Data.MySqlClient");  
  200.   providerInvariantNames.Add(DbProviderType.SQLite, "System.Data.SQLite");  
  201.   providerInvariantNames.Add(DbProviderType.Firebird, "FirebirdSql.Data.Firebird");  
  202.   providerInvariantNames.Add(DbProviderType.PostgreSql, "Npgsql");  
  203.   providerInvariantNames.Add(DbProviderType.DB2, "IBM.Data.DB2.iSeries");  
  204.   providerInvariantNames.Add(DbProviderType.Informix, "IBM.Data.Informix");  
  205.   providerInvariantNames.Add(DbProviderType.SqlServerCe, "System.Data.SqlServerCe");  
  206.  }  
  207.  /// <summary>  
  208.  /// 获取指定数据库类型对应的程序集名称  
  209.  /// </summary>  
  210.  /// <param name="providerType">数据库类型枚举</param>  
  211.  /// <returns></returns>  
  212.  public static string GetProviderInvariantName(DbProviderType providerType)  
  213.  {  
  214.   return providerInvariantNames[providerType];  
  215.  }  
  216.  /// <summary>  
  217.  /// 获取指定类型的数据库对应的DbProviderFactory  
  218.  /// </summary>  
  219.  /// <param name="providerType">数据库类型枚举</param>  
  220.  /// <returns></returns>  
  221.  public static DbProviderFactory GetDbProviderFactory(DbProviderType providerType)  
  222.  {  
  223.   //如果还没有加载,则加载该DbProviderFactory  
  224.   if (!providerFactoies.ContainsKey(providerType))  
  225.   {  
  226.    providerFactoies.Add(providerType, ImportDbProviderFactory(providerType));  
  227.   }  
  228.   return providerFactoies[providerType];  
  229.  }  
  230.  /// <summary>  
  231.  /// 加载指定数据库类型的DbProviderFactory  
  232.  /// </summary>  
  233.  /// <param name="providerType">数据库类型枚举</param>  
  234.  /// <returns></returns>  
  235.  private static DbProviderFactory ImportDbProviderFactory(DbProviderType providerType)  
  236.  {  
  237.   string providerName = providerInvariantNames[providerType];  
  238.   DbProviderFactory factory = null;  
  239.   try 
  240.   {  
  241.    //从全局程序集中查找  
  242.    factory = DbProviderFactories.GetFactory(providerName);  
  243.   }  
  244.   catch (ArgumentException e)  
  245.   {  
  246.    factory = null;  
  247.   }  
  248.   return factory;  
  249.  }  


用法举例,访问SQLite数据库:
 

 
  1. string connectionString = @"Data Source=D:\VS2008\NetworkTime\CrawlApplication\CrawlApplication.db3";  
  2. string sql = "SELECT * FROM Weibo_Media order by Id desc limit 0,20000";  
  3. DbUtility db = new DbUtility(connectionString, DbProviderType.SQLite);  
  4. DataTable data = db.ExecuteDataTable(sql, null);  
  5. DbDataReader reader = db.ExecuteReader(sql, null);  
  6. reader.Close(); 


用法举例,访问MySQL:
 

 
  1. string connectionString = @"Server=localhost;Database=crawldb;Uid=root;Pwd=root;Port=3306;";  
  2. string sql = "SELECT * FROM Weibo_Media order by Id desc limit 0,20000";  
  3. DbUtility db = new DbUtility(connectionString, DbProviderType.MySql);  
  4. DataTable data = db.ExecuteDataTable(sql, null);  
  5. DbDataReader reader = db.ExecuteReader(sql, null);  
  6. reader.Close(); 


从上面的代码可以看出,使用这个通用的数据库通用类和以前针对特定的数据库通用类没有什么区别,这一切都是DbProviderFactory这个类的功劳。
总结
设计模式在编程中对于应对变化确实非常有用,因为在ADO.NET中存在着这么一个工厂模式,轻而易举地就解决了针对不同数据库访问的问题。


















本文转自周金桥51CTO博客,原文链接:http://blog.51cto.com/zhoufoxcn/622376 ,如需转载请自行联系原作者





相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
23天前
|
NoSQL 关系型数据库 MySQL
微服务架构下的数据库选择:MySQL、PostgreSQL 还是 NoSQL?
在微服务架构中,数据库的选择至关重要。不同类型的数据库适用于不同的需求和场景。在本文章中,我们将深入探讨传统的关系型数据库(如 MySQL 和 PostgreSQL)与现代 NoSQL 数据库的优劣势,并分析在微服务架构下的最佳实践。
|
23天前
|
设计模式 缓存 关系型数据库
探索微服务架构中的数据库设计挑战
微服务架构因其模块化和高扩展性被广泛应用于现代软件开发。然而,这种架构模式也带来了数据库设计上的独特挑战。本文探讨了在微服务架构中实现数据库设计时面临的问题,如数据一致性、服务间的数据共享和分布式事务处理。通过分析实际案例和提出解决方案,旨在为开发人员提供有效的数据库设计策略,以应对微服务架构下的复杂性。
|
23天前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与最佳实践
在微服务架构中,数据库访问的效率直接影响到系统的性能和可扩展性。本文探讨了优化微服务架构中数据库访问的策略与最佳实践,包括数据分片、缓存策略、异步处理和服务间通信优化。通过具体的技术方案和实例分析,提供了一系列实用的建议,以帮助开发团队提升微服务系统的响应速度和稳定性。
|
12天前
|
人工智能 网络协议 Shell
内网穿透实现公网访问自己搭建的Ollma架构的AI服务器
内网穿透实现公网访问自己搭建的Ollma架构的AI服务器
28 1
|
12天前
|
人工智能 网络协议 Shell
内网穿透实现公网访问自己搭建的Ollma架构的AI服务器
内网穿透实现公网访问自己搭建的Ollma架构的AI服务器
26 0
内网穿透实现公网访问自己搭建的Ollma架构的AI服务器
|
23天前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与实践
随着微服务架构的普及,如何高效管理和优化数据库访问成为了关键挑战。本文探讨了在微服务环境中优化数据库访问的策略,包括数据库分片、缓存机制、异步处理等技术手段。通过深入分析实际案例和最佳实践,本文旨在为开发者提供实际可行的解决方案,以提升系统性能和可扩展性。
|
1天前
|
数据库
Admin.Net根据域名自动选择数据库
Admin.Net根据域名自动选择数据库
6 0
|
1天前
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
7 0
|
1月前
|
SQL 存储 关系型数据库
C#一分钟浅谈:使用 ADO.NET 进行数据库访问
【9月更文挑战第3天】在.NET开发中,与数据库交互至关重要。ADO.NET是Microsoft提供的用于访问关系型数据库的类库,包含连接数据库、执行SQL命令等功能。本文从基础入手,介绍如何使用ADO.NET进行数据库访问,并提供示例代码,同时讨论常见问题及其解决方案,如连接字符串错误、SQL注入风险和资源泄露等,帮助开发者更好地利用ADO.NET提升应用的安全性和稳定性。
84 6