告别ADO.NET实现应用系统无缝切换的烦恼(总结篇)

可能只是那么想也是那么设计的,要支持多数据库,要能支持多数据库,万一要是以后数据库变了怎么办?万一要是。。。怎么办?这些顾虑很多时候是不必要的,反而绕了弯子。大都是做项目应用系统而非产品,即使要用不同的数据库了,基本上是吧上一个项目全COPY过来,修修改改OK了。产品可能就不一样了,那才可能要支持真正的多数据库,才可能会面对真正的数据库访问类库的多数据库的实际检验。ADO.NET2.0下增强了数据库访问的功能,也就是工厂式类库,提到工厂式数据库访问,网上可就多了,ADO.NET2.0增强的工厂式网上也很多了,都说只要改动webconfig里的数据库连接就行了,其它什么地方都不用改了,看了几篇都是点了下,不知道做过充分测试没有,应该说在实际的多数据库产品系统中,还要做很多修正,完善和测试的。
说正题,假设(只是假设,真的不会这么变态,呵呵)一产品系统要支持ACCESS,SYBASE,SQL SERVER,ORACEL数据库,系统开发完后,要求只改动数据库的连接,也就是说只改动webconfig,来实现系统的无缝切换,其它什么也不改动,可能有的觉得简单,但是要经得起实际检验还是要花点时间测试的,当然写是不算难,见到过有的应用系统开发中,所有的DML语句都以XML形式放在config配置文件里,然后用封装好的数据库访问组件读config配置文件执行SQL语句,开发人员只要建个config文件把增删改查语句写那里就可以,当然这么来有个好处,就是做到了与数据库的无关性了,如果数据库访问组件做的完善的,当然是可以做到系统无缝切换到其它数据库的,当然同时也有缺陷,项目中不仅仅是DML语句吧,有的稍微复杂点的逻辑,存储过程要用下吧,我自己也趋向于喜欢用存储过程,自定义函数来处理稍微复杂的逻辑,而且把DML语句都放在配置文件里我也是不能忍受的,可能是一个个人的习惯吧。
继续,下面开始我要说的利用ADO.NET2.0及以上版本新增的工厂式数据库访问实现应该系统的无缝切换,要实现无缝切换,当然还是要有前提条件了,就是各个不同的数据库之间的表和其它对象都已经成功移植了,没有这个前提,纯用ADO.NET做系统无缝切换那是不可能的了,比如SQL SERVER中写的存储过程,自定义函数直接复制到ORACLE上就行了吗?当然是不行,写法及变量定义要做些调整才可以成功移植的,还有变结构字段类型等等的都可能是要做相应调整,这些都做好了才能谈系统的无缝切换。要做的无缝切换,数据库访问层的代码中最好(并非绝对)不应该出现SqlCommand,SqlDataAdapter,SqlClient,SqlXXX吧,要切换到ORACLE数据上,甲骨文会把你的SqlXXX玩死的,ORACLE里可以OracleCommand,OracleXXX,还有程序执行带参数语句时,比如 where userid=@userid,甲骨文也会玩死你的,oracle里可是where userid=:userid,@前缀在ACCESS,SYBASE,SQL SERVER里是都认得,还有还有字段名的写法问题,ORACLE里可以区分大小写的,比如可能大多习惯这样命名属性和自段,UserName,UserAge,如果在ORACLE里这么命名的话,系统开发过程中的那种痛苦也许只有经历过的人才知道,ORACLE坚持大写为标准,记得很久很久以前的一个夏天的晚上,那时我还是年轻的80后,一位数据库设计比较N的人提到过,尽量在数据库设计和T-SQL编程中采用大写标准,基本上接触的SQL SERVER数据库较多,也习惯了表名,字段名的大写设计,后来发现确实是有道理的。这里提到的问题都是在下面的各个方法中为了兼容不同的数据库需要面对的问题,具体讲到每个执行方法时再具体解释。刚才说SqlCommand,OracleComand都是各自认得,但是DbCommand可是大家都认得的,暂且叫抽象对象吧,还有DbConnection,DbDataAdapter等都是他们都认得的,所以在做支持多数据库访问类库时,就可以用这些对象了,根据这些对象再创建具体对象。ADO.NET2.0中数据库访问工厂中有个 DbProviderFactory 对象,也就是通常说的DataProvider了,正是这个起了关键和方便的作用,是用来创建提供程序对数据源类的实现的实例(就是用来创建实例)。另外数据库操作还要用到参数吧,DbParameter,DbParameterCollection下面都需要用到,先贴一段类库的构造函数,因为共用对象需要先实例化。


代码如下:

public DbConnection conn;//抽象类型
private DbCommand cmd;//抽象类型
private DbProviderFactory provider;
private DbParameter Para;//不同数据库参数类型的抽象类型
private DbDataAdapter Adapter;//对应不同数据库的数据适配器
Dictionary<Type, String> ParametersFormat;//不同数据库参数格式化类型
public string retParaformat = string.Empty;//最终返回的格式化标志,如@{0},:{0}
public DataProviderFactory()
{
//从配置文件中取出标示数据库类型的字符串并通过ProviderName的不同支持不同类型的数据库
string providerName = ConfigurationManager.ConnectionStrings["ConnStr"].ProviderName;//也可以用索引,从1开始
//创建一个数据库对应的实例,使用该实例就可以创建对应的connection,command 和adapater等等对象
provider = DbProviderFactories.GetFactory(providerName);
//创建具体的数据库连接类型和命令执行类型
conn = provider.CreateConnection();
conn.ConnectionString = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
cmd = provider.CreateCommand();
cmd.Connection = conn;
//创建具体的参数类型
Para = provider.CreateParameter();
//创建具体的适配器类型
Adapter = provider.CreateDataAdapter();
//不同数据库参数前缀格式化
ParametersFormat = new Dictionary<Type, String>();
ParametersFormat.Add(typeof(System.Data.SqlClient.SqlCommand), "@{0}");//因SQL SERVER只返回{0}没有@前缀,在此初始化处理
//返回格式化标志
retParaformat = GetParameterFormat(cmd);
}

上面那段代码中,可以看到我定义了两个公共变量,其中conn在外部只会有一个地方调用它,那就就是执行DataReader方法的时候了,因为大家都知道dr在离开方法体时,连接是不能关闭的,
所以只能在外部调用处显示关闭连接对象 ,必须定义为公共类型了,还有一个公共参数变量是格式化字符串的字符型。前面说到Oracle参数前缀是冒号:,其它几个数据库前缀是@符号,怎么样在切换数据库以后程序能动态识别参数前缀并组合相应的参数变量呢?如果手动写代码用数据库对象类型枚举去一个个判断,那这数据库工厂也没什么意义了,最终找到了一个相当完美的解决方式(微软就是微软,都能替你想到,别人想不强大都难啦,呵呵),其实在做测试的时侯到各不同数据库的参数前缀这就有点犯难了,手写代码一个个处理吧,没问题,觉得应该有简单的方法吧,MS从来就不是傻瓜呀,正好前两天在园子首页就有篇提到这个问题,而且给出相关提示,根据给的提示再gg了一把,终于找到了一个便捷的办法。还是贴方法代码好了,如下:


代码如下:

/// <summary>
/// 根据不同的数据库命令对象返回该类型数据库参数的前缀格式化字符串
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
private string GetParameterFormat(DbCommand command)
{
if (!ParametersFormat.ContainsKey(command.GetType()))
{
this.Open();//读取参数前缀时需打开数据库连接
ParametersFormat.Add(
command.GetType(),
command.Connection.GetSchema("DataSourceInformation")
.Rows[0]["ParameterMarkerFormat"].ToString());
//conn.Close();在真正执行语句的时候去关闭,避免重复打开
}
return ParametersFormat[command.GetType()];
}

就是这个了 ParameterMarkerFormat,即参数标志符格式化,如连接oracle数据库则返回:{0},其它几个数据库返回@{0},惟独SQL SERVER数据库返回{0},到底是MS自己的东西,就是要返回跟别人不一样的东西,也就因为这个,这个类库里很遗憾不得不出现一个SqlCommand,就是上面贴出的构造函数里的初始化那ParametersFormat.Add(typeof(System.Data.SqlClient.SqlCommand), "@{0}");必须这样做下处理,另外包括GetParameterFormat方法里的判断,即不是SQL SERVER数据库时才去读参数前缀,如果是就直接返回@{0},有了这个格式化的前缀字符串,就好办了.那参数名称的赋值就可以类似这样了string.Format("@{0}", ParaName);
下面说说各通用的方法和调用,之前的sqlhelper.cs,oraclehelper.cs,xxhelper.cs中的执行方法大多都很多,有带参数执行的语句的方法,不带参数执行的语句的方法,带参数执行的方法体里面还要循环参数,这些都我都精简掉了,最终演变成了peacehelper.cs(开个玩笑).带参执行和不带参执行DML语句,其实是可以合并成一个方法,各个参数都是保存在数据库命令对象的参数集合中的,我们可以把创建好的命令对象返回给外部程序调用处,调用的地方要带参执行语句的话,就定义参数并赋值就行了,不带参执行的话就不用定义参数了,这么以来就只需要写一个方法就行了,而且执行带掺的语句时不用再循环参数集合了,因为在调用处定义参数时,该参数已经绑定都了DbCommand对象了.写一个返回给外部调用的数据库命令对象的方法,如下:


代码如下:

/// <summary>
/// 抽象参数集合类型
/// </summary>
/// <returns></returns>
public DbParameterCollection GetParmCollection()
{
return cmd.Parameters;
}

添加参数的方法如下:


代码如下:

/// <summary>
/// 添加参数
/// </summary>
/// <param name="ParaName">参数名称</param>
/// <param name="SqlType">参数数据类型</param>
/// <param name="ParaValue">参数值</param>
/// <param name="ParaCollect">参数对象的集合</param>
public void AddParam(string ParaName, DbType SqlType, object ParaValue, DbParameterCollection ParaCollect)
{
//不允许将一个DbCommand对象的Parameters插入到另外一个DbCommand对象,那么多个参数的话可以加上下面一句判断
//如果已经存在至少一个对象时,再深层拷贝一个
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat, ParaName);
Para.DbType = SqlType;
if (ParaValue == null)
{
Para.Value = string.Empty;//DBNull.Value;
}
else
{
Para.Value = ParaValue;
}
ParaCollect.Add(Para);
}

上面有句判断,如果有多个参数会出异常,网上搜了下,注释就是网上的解释,不多说了,意思很清楚。这个方法里还有一点,如果DbType参数不要的话测试也是可以通过的,猜想如果不显示指定参数数据类型的话,是不是都默认为object类型?这样的话会不会涉及一个装拆箱的操作呢?但是开发人员在调用处添加参数,是不应该关心参数的数据类型才对,干脆数据类型参数不要了,改成如下方法了:


代码如下:

public void AddParam(string ParaName, object ParaValue, DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat, ParaName);//将参数格式化为具体的数据库参数格式
if (ParaValue == null)
{
Para.Value = string.Empty;
}
else
{
Para.Value = ParaValue;
}
ParaCollect.Add(Para);
}

为了兼容不同的数据库(主要是oracle变量特殊问题),添加参数的方法分两种,一种是普通带参执行的DML语句,一种是代参执行的存储过程。对于SQL SERVER数据库即使是存储过程
变量参数仍是@前缀,ORACLE存储过程又是什么前缀呢?很遗憾,ORACLE存储过程的参数变量是不需要任何前缀的,为了单独兼容这一点,对于不同数据库如果调用的存储过程有参数
的话,建议用下面的三个添加参数的方法:


代码如下:

/// <summary>
/// 存储过程输入参数
/// </summary>
/// <param name="ParaName"></param>
/// <param name="ParaValue"></param>
/// <param name="ParaCollect"></param>
public void AddInputParam(string ParaName, object ParaValue, DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat.Replace(":",""), ParaName);//ORACLE存储过程参数前没有冒号
if (ParaValue == null)
{
Para.Value = string.Empty;
}
else
{
Para.Value = ParaValue;
}
ParaCollect.Add(Para);
}
/// <summary>
/// 存储过程输出参数
/// </summary>
/// <param name="ParaName"></param>
/// <param name="ParaValue"></param>
/// <param name="ParaCollect"></param>
public void AddOutputParam(string ParaName, DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat.Replace(":", ""), ParaName);
Para.Value = string.Empty;
ParaCollect.Add(Para);
ParaCollect[Para.ParameterName].Direction = System.Data.ParameterDirection.Output;//指定该参数为输出参数
}
/// <summary>
/// 存储过程返回值参数
/// </summary>
/// <param name="ParaName"></param>
/// <param name="ParaValue"></param>
/// <param name="ParaCollect"></param>
public void AddReturnParam(string ParaName,DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat.Replace(":", ""), ParaName);
Para.Value = string.Empty;
ParaCollect.Add(Para);
ParaCollect[Para.ParameterName].Direction = System.Data.ParameterDirection.ReturnValue;//指定该参数为返回值参数
}

OK,现在开始说下peacehelper.cs里的八大方法(其实算起来应该是10个),应该来说涵盖绝大多应该系统操作数据库的绝大部分功能,如果有特殊的操作可以在此基础上添加。
第一个,大家都熟悉的返回结果集:


代码如下:

/// <summary>
/// 执行SQL并返回数据集
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public DataSet ExecDataSet(string Sql)
{
DataSet ds = new DataSet();
try
{
this.Open();
cmd.CommandText = Replace(Sql);
Adapter.SelectCommand = cmd;
Adapter.Fill(ds);
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
return ds;
}

上面的方法大家看了是不是觉得既简单又熟悉,确实是的,但仍然相当以前的xxhelper.cs里是做了简化的,该方法既可以直接执行不带参DML语句,也可以执行带参的,但是该方法的形参却
只有一个,之前的xxhelper.cs里带参执行的话,形参中大多至少还另外一个形参的,比如SqlPeremeters[]类型或参数集合类型的形参,而且方法体里面大多会循环读取参数,上面的方法里
却没有,都简化掉了,唯一多了一点的是,所执行的命令语句执行前要做一个特殊字符替换,cmd.CommandText = Replace(Sql),Replace方法主要是替换参数前缀,Replace方法如下:


代码如下:

/// <summary>
/// 替换DML语句里的参数前缀
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public string Replace(string str)
{
return str.Replace("$", retParaformat.Substring(0, 1));
}

因为不同数据库除了在添加参数时有前缀的区别,再具体执行语句时也有前缀区别嘛,比如SQL SERVER里 SELECT USER_NAME,USER_AGE FROM USERS WHERE USER_ID=@USER_ID,ORACLE里是这样的SELECT USER_NAME,USER_AGE FROM USERS WHERE USER_ID=:USER_ID,在此就需要统一一个前缀规则了,统一这样SELECT USER_NAME,USER_AGE FROM USERS WHERE USER_ID=$USER_ID,在执行前根据不同数据库替换前缀$符号,当然这个约定规则不一定是最完美的,也许还存在一定的问题,写到这我也突然想起来之前我见过别人
的系统中有的就是变量参数用的这种类似特殊符号,肯定也是为了兼容多数据库所作的处理了,呵呵,具体的调用及测试之后统一说明。还有几个方法也和上面类似,大家都熟悉的。如下(不再做具体解释了):


代码如下:

/// <summary>
/// 执行SQL语句并返回DataReader对象
/// </summary>
/// <param name="dbcon"></param>
/// <param name="cmdText"></param>
/// <returns></returns>
public DbDataReader ExecuteDataReader(DbConnection dbcon,string cmdText)
{
try
{
if (dbcon.State == ConnectionState.Closed)
{
dbcon.Open();
}
cmd.CommandText = Replace(cmdText);
DbDataReader dr = cmd.ExecuteReader();
cmd.Parameters.Clear();
cmd.Dispose();
return dr;
}
catch
{
dbcon.Close();//发生异常在此处关闭,否则在调用显式处关闭
return null;
}
}

/// <summary>
/// 判断记录是否存在
/// </summary>
/// <param name="Sql"></param>
/// <returns></returns>
public bool Exist(string Sql)
{
bool exist;
this.Open();
cmd.CommandText = Replace(Sql);
DbDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
exist = true; //记录存在
}
else
{
exist = false; //记录不存在
}
dr.Close();
this.Close();
return exist;
}
/// <summary>
/// 执行SQL语句
/// </summary>
/// <param name="sql"></param>
public void ExecSql(string Sql)
{
try
{
this.Open();
cmd.CommandText = Replace(Sql);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}

}
/// <summary>
/// 执行SQL语句,返回一个单值
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public string ReturnValue(string Sql)
{
object returnValue = string.Empty;
try
{
this.Open();
cmd.CommandText = Replace(Sql);
returnValue = cmd.ExecuteScalar();
if (returnValue == null)
{
returnValue = string.Empty;
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
return returnValue.ToString();
}
/// <summary>
/// 执行多条SQL语句并启用数据库事务
/// </summary>
/// <param name="SQLStringList"></param>
public bool ExecSqlTran(List<String> SQLStringList)
{
this.Open();
DbTransaction trans = conn.BeginTransaction();
cmd.Transaction = trans;
try
{
for (int n = 0; n < SQLStringList.Count; n++)
{
cmd.CommandText = Replace(SQLStringList[n]);
cmd.ExecuteNonQuery();
}
trans.Commit();
return true;
}
catch
{
trans.Rollback();
return false;
}
finally
{
this.Close();
}
}

下面说下两个存储过程,存储过程基本上分两种,返回结果集的存储过程和执行业务逻辑不返回结果集但却有返回值(如标志等),对于需要有返回值的存储过程,我个人趋向于用输出
参数代替返回值,因为都能达到一样的效果目的,而且输出参数可以有多个,也就可以根据需要能有多个所谓的“返回值”,所以我之前的开发中一直是用output参数来代替return参数。

代码如下:

/// <summary>
/// 执行存储过程并返回结果集
/// </summary>
/// <param name="storedProcName">存储过程名</param>
/// <returns>DataSet</returns>
public DataSet RunProcedure(string storedProcName)
{
DataSet ds = new DataSet();
try
{
this.Open();
cmd.CommandText = storedProcName;
cmd.CommandType = CommandType.StoredProcedure;
Adapter.SelectCommand = cmd;
//Adapter.SelectCommand.CommandTimeout = 1200;//可以设置适当的超时时间(秒),避免选择时间段过大导致填充数据集超时
Adapter.Fill(ds);
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
return ds;
}
/// <summary>
/// 执行存储过程,方法不返回结果集
/// </summary>
/// <param name="storedProcName"></param>
public void RunVoidProcedure(string storedProcName)
{
cmd.CommandText = storedProcName;
cmd.CommandType = CommandType.StoredProcedure;
try
{
this.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
}

下面说两个反射方法,测试之后为了方便调用,减少操作添加的,一个是把实体类的属性转换为参数,另一个是把从数据库取出的某条记录转换为实体类,这两个还是非常有用,尤其是在系统开发时调用比较方便,以前我是见到反射就绕道走的,这次算是第一次用反射,发现确实是很方便。如下:


代码如下:

/// <summary>
/// 将实体类的属性进行参数转换(ORACLE测试通不过,必须要求所有参数都包含在语句中才行)
/// </summary>
/// <param name="model"></param>
/// <param name="ParaCollect"></param>
//public void ConvertToParameters(object model, DbParameterCollection ParaCollect)
//{
// Type T = model.GetType();
// PropertyInfo[] propert = T.GetProperties();
// for (int i = 0; i < propert.Length; i++)
// {
// AddParam(propert[i].Name, propert[i].GetValue(model, null), ParaCollect);
// }
//}
/// <summary>
/// 将实体类的属性进行参数转换
/// </summary>
/// <param name="model"></param>
/// <param name="ParaCollect"></param>
public void ConvertToParameters(object model, DbParameterCollection ParaCollect,List<string> fields)
{
Type T = model.GetType();
PropertyInfo[] propert = T.GetProperties();
for (int i = 0; i < propert.Length; i++)
{
if (fields.Contains(propert[i].Name)) //检测必须参数化的实体属性
{
AddParam(propert[i].Name, propert[i].GetValue(model, null), ParaCollect);
}
}
}

/// <summary>
/// 通过反射将取出的数据写入实体类(ORACLE测试通不过,需进行类型强制转换)
/// </summary>
/// <param name="model"></param>
/// <param name="cmdText"></param>
//public void GetModel(object model, string cmdText)
//{
// PropertyInfo propertyInfo;
// DbDataReader dr = ExecuteDataReader(conn, cmdText);
// while (dr.Read())
// {
// for (int i = 0; i < dr.FieldCount; i++)
// {
// propertyInfo = model.GetType().GetProperty(dr.GetName(i));
// if (propertyInfo != null)
// {
// if (dr.GetValue(i) != DBNull.Value)
// {
// //Type t = dr.GetValue(i).GetType();
// propertyInfo.SetValue(model, dr.GetValue(i), null);
// }
// }
// }
// }
// dr.Close();
// conn.Close();
//}
/// <summary>
/// 通过反射将数据绑定到实体对象,由于不同数据库对应于.NET的数据类型不一样
/// 需做强制类型转换
/// </summary>
/// <param name="model"></param>
/// <param name="cmdText"></param>
public void GetModel(object model, string cmdText)
{
PropertyInfo propertyInfo;
DbDataReader dr = ExecuteDataReader(conn, cmdText);
object _value;
while (dr.Read())
{
for (int i = 0; i < dr.FieldCount; i++)
{
propertyInfo = model.GetType().GetProperty(dr.GetName(i));
if (propertyInfo != null && dr.GetValue(i) != DBNull.Value)
{
switch (propertyInfo.PropertyType.ToString())
{
case "System.String":
{
_value = Convert.ToString(dr.GetValue(i));//字符串是全球通用类型,也可以不用转换
propertyInfo.SetValue(model, _value, null);
}break;
case "System.Int32":
{
_value = Convert.ToInt32(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "System.Single":
{
_value = Convert.ToSingle(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "System.Decimal":
{
_value = Convert.ToDecimal(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "System.Double":
{
_value = Convert.ToDouble(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "":
{
_value = Convert.ToDateTime(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
default: break;
}
}
}
}
dr.Close();
conn.Close();
}

从上面的注释掉的方法对比中可以看到为了兼容不同的数据库,必须要做额外的处理,比如类型转换,SQL SERVER的int 对应ORALCE的number,UserInfo的字段属性UserAge定义的是int类型,连接ORALCE时,.NET识别number类型为System.Decimal,把Decimal赋值给Int32当然是不行的,所以得做强制转换才行。还有一点要注意下,就是将数据绑定到实体对象时,由于ORACLE坚持大写标准和解析机制,如果属性名和字段名大小写不一致的话,propertyInfo = model.GetType().GetProperty(dr.GetName(i)) ,propertyInfo 始终是null值,比如SELECT UserName,UserAge FROM USER_TEST WHERE USERID=$USERID,SQL SERVER 执行的时候调试可以看到dr.GetName(0)是UserName,dr.GetName(1)是UserAge,ORACLE执行解析就变了,全是大写了,变成了USERNAE,USERAGE,这么一来和找不到UserInfo类的属性了,因为UserInfo类的属性是 UserName,和UserAge,C#语言变量也是区分大小写的嘛,当然就找不到了,所以propertyInfo就为null了,故在这里再次建议大家在数据库设计和程序字段属性设计时采用大写标准(如果不涉及多数据库当然也不需要这么做)。
最后说下测试调用代码,首先webconfig配置里面这样配置下,主要选取SQL SERVER和ORACLE做测试,毕竟这是.NET支持的两个典型数据库,要是把.NET所支持的所有书库都测试一遍,那测试量可不小了,呵呵。

代码如下:

<connectionStrings>
<add name="ConnStr" connectionString="uid=sa;pwd=peace;database=TEST;server=." providerName="System.Data.SqlClient" />
<!--<add name="ConnStr" connectionString="server=.;data source=peace;user id=cct;password=cct;enlist=true" providerName="System.Data.OracleClient"/>-->
</connectionStrings>

protected void Page_Load(object sender, EventArgs e)
{
//测试DataReader,SQLSERVER和ORACLE都通过
//DataProviderFactory fac = new DataProviderFactory();
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam("USERID", 100, ParaCollect);
//DbDataReader dr = fac.ExecuteDataReader(fac.conn, "SELECT * FROM USER_TEST WHERE USERID=$USERID");
//while (dr.Read())
//{
// string a = dr[1].ToString();
//}
//fac.conn.Close();//在调用处显示关闭

//无参数DataSet测试 SQLSERVER和ORACLE都通过
//DataTable dt = fac.ExecDataSet("SELECT * FROM USER_TEST").Tables[0];
//带参数DataSet测试 SQLSERVER和ORACLE都通过
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam("USERID", 100, ParaCollect);
//fac.AddParam("USERNAME", "局%", ParaCollect);//这里的参数名可以任意成其它,不一定非要和字段名相同(下同)
//DataTable dt = fac.ExecDataSet("SELECT * FROM USER_TEST WHERE USERNAME LIKE $USERNAME").Tables[0];
//DataTable dt = fac.ExecDataSet("SELECT * FROM USER_TEST WHERE USERID=$USERID OR USERNAME LIKE $USERNAME").Tables[0];//多参数测试
//单值测试(带参数) SQLSERVER和ORACLE都通过
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam("USERID", 100, ParaCollect);
//string retValue = fac.ReturnValue("SELECT USERNAME FROM USER_TEST WHERE USERID=$USERID");
//带参存储过程测试返回结果集 SQLSERVER和ORACLE都通过
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam("StartDate", "2009-8-1", ParaCollect);
//fac.AddParam("EndDate", "2009-8-21", ParaCollect);
//DataTable dt = fac.RunProcedure("USP_GetMixedReport").Tables[0];

//带参数测试存储过程的输出参数值和返回值,方法不返回结果集 SQLSERVER通过
//int flag = 0, sign = 0, ret = 0;
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam("USER_ACCOUNT", DbType.String, "admin", ParaCollect);
//fac.AddParam("USER_PWD", DbType.String, "68053af2923e00204c3ca7c6a3150cf7", ParaCollect);
//fac.AddParam("FLAG", DbType.Int32, "", ParaCollect);
//ParaCollect["@FLAG"].Direction = System.Data.ParameterDirection.Output;
//fac.AddParam("SIGN", DbType.Int32, "", ParaCollect);
//ParaCollect["@SIGN"].Direction = System.Data.ParameterDirection.Output;
//fac.AddParam("RetValue", DbType.String, "", ParaCollect);
//ParaCollect["@RetValue"].Direction = System.Data.ParameterDirection.ReturnValue;
//fac.RunVoidProcedure("SP_ValideLogin");
//flag = int.Parse(ParaCollect["@FLAG"].Value.ToString());
//sign = int.Parse(ParaCollect["@SIGN"].Value.ToString());
//ret = int.Parse(ParaCollect["@RetValue"].Value.ToString());//存储过程约定返回值必须是int型

//改进后带参数测试存储过程的输出参数值和返回值的测试 SQLSERVER和ORACLE都通过
//int flag = 0, sign = 0, ret = 0;
//DataProviderFactory fac = new DataProviderFactory();
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddInputParam("USER_ACCOUNT", "admin", ParaCollect);
//fac.AddInputParam("USER_PWD", "68053af2923e00204c3ca7c6a3150cf7", ParaCollect);
//fac.AddOutputParam("FLAG", ParaCollect);
//fac.AddOutputParam("SIGN", ParaCollect);
//fac.AddReturnParam("RetValue", ParaCollect);
//fac.RunVoidProcedure("SP_ValideLogin");
//string prefix = fac.retParaformat.Replace(":","");//Oracle存储过程参数前冒号移除掉
//flag = int.Parse(ParaCollect[string.Format(prefix,"FLAG")].Value.ToString());
//sign = int.Parse(ParaCollect[string.Format(prefix, "SIGN")].Value.ToString());
//ret = int.Parse(ParaCollect[string.Format(prefix, "RetValue")].Value.ToString());//存储过程约定返回值必须是int型

//调用存储过程测试 SQLSERVER和ORACLE都通通过
//DataProviderFactory fac = new DataProviderFactory();
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddInputParam("P_UserID", 7, ParaCollect);
//fac.AddInputParam("P_UserName", "peace", ParaCollect);
//fac.AddInputParam("P_UserAge", 100, ParaCollect);
//fac.RunVoidProcedure("PROC_USER_TEST_ADD");
//多条提交事务处理测试 SQLSERVER和ORACLE都通过
//List<string> SqlList = new List<string>();
//DataProviderFactory fac = new DataProviderFactory();
//DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam("UserName", "peaceli", ParaCollect);
//fac.AddParam("UserAge", 150, ParaCollect);
//SqlList.Add("INSERT INTO USER_TEST(UserName,UserAge) VALUES($UserName,$UserAge)");
//SqlList.Add("INSERT INTO USER_TEST(UserName,UserAge) VALUES($UserName,$UserAge)");
//SqlList.Add("INSERT INTO USER_TEST(UserName,UserAge) VALUES($UserName,$UserAge)");
//fac.ExecSqlTran(SqlList);
//插入操作参数测试(SQL SERVER) 通过
//UserInfo ui = new UserInfo();
//ui.UserName = "hello peace";
//ui.UserAge = 100;
//Addinn(ui);
//插入操作参数测试(Oracle) 通过
//UserInfo ui = new UserInfo();
//ui.USERID = 10;
//ui.USERNAME = "hello peace";
//ui.USERAGE = 120;
//Addin(ui);
//插入操作反射参数转换测试 SQLSERVER和ORACLE都通过
//UserInfo ui = new UserInfo();
//ui.USERNAME = "peaceli";
//ui.USERAGE = 110;
//Add(ui);
//返回实体对象测试 SQLSERVER和ORACLE都通过
UserInfo ui = new UserInfo();
ui.USERID = 1;
GetInfo(ui);
}
//private void Addinn(UserInfo ui)
//{
// DataProviderFactory fac = new DataProviderFactory();
// DbParameterCollection ParaCollect = fac.GetParmCollection();
// fac.AddParam("@UserName", ui.UserName, ParaCollect);
// fac.AddParam("@UserAge", ui.UserAge, ParaCollect);
// fac.ExecSql("INSERT INTO USER_TEST(UserName,UserAge) VALUES(@UserName,@UserAge)");
//}
private void Addin(UserInfo ui)
{
DataProviderFactory fac = new DataProviderFactory();
DbParameterCollection ParaCollect = fac.GetParmCollection();
//fac.AddParam(":UserName", ui.UserName, ParaCollect);//给参数赋值时冒号可以不加,但有的版本可能必须加
//fac.AddParam(":UserAge", ui.UserAge, ParaCollect);
//fac.AddParam("UserID", ui.USERID, ParaCollect); //这行注释放开在ORACLE下同不过,ORACLE要求所全参数匹配,有多余参数就不行,这点有些变态
fac.AddParam("UserName", ui.USERNAME, ParaCollect);//SQL SERVER只要求用到的参数包含在参数集合里就行了,其它多余参数并不影响执行
fac.AddParam("UserAge", ui.USERAGE, ParaCollect);
fac.ExecSql("INSERT INTO USER_TEST(UserName,UserAge) VALUES(:UserName,:UserAge)");
}
private void Add(UserInfo ui)
{
DataProviderFactory fac = new DataProviderFactory();
DbParameterCollection ParaCollect = fac.GetParmCollection();
string[] fields = { "USERNAME", "USERAGE" };//要求参数化的实体属性
List<string> ListFields = new List<string>(fields);
fac.ConvertToParameters(ui, ParaCollect, ListFields);//如果新增记录有很多参数的话,可能AddParam很多次,采用反射批量转换
fac.ExecSql("INSERT INTO USER_TEST(USERNAME,USERAGE) VALUES($USERNAME,$USERAGE)");
}
private void GetInfo(UserInfo ui)
{
DataProviderFactory fac = new DataProviderFactory();
DbParameterCollection ParaCollect = fac.GetParmCollection();
fac.AddParam("USERID", ui.USERID, ParaCollect);
fac.GetModel(ui, "SELECT USERNAME,USERAGE FROM USER_TEST WHERE USERID=$USERID");
}
}

UserInfo类如下:

代码如下:

public class UserInfo
{
public int USERID { get; set; }
public string USERNAME { get; set; }
public int USERAGE { get; set; }
}

测试到最后类属性改动过,统一改成了大写,再次建议大写标准(包括数据库设计),可以定义成USER_ID,USER_NAME,USER_AGE等,并与数据库字段名保持一致,这样有利于多数据库的
兼容。
结语:个人并不反对项目里单独用对应的xxhelper.cs,某个项目用SQLSERVER数据库,就用SqlHelper.csL类,ORACLE就用OracleHelper.cs类,这样来得更干脆快捷,基本上每个项目都是这对特定的数据库在开发,没必要搞成通用类,真要搞成通用类,要经过大量的实际测试,也许我最近有时寂寞空虚也无聊,突然想测试下同时也想改进下,呵呵,零零碎碎花了点时间测试了下,选取两个数据库测试了一遍,最终只需要改动config配置的数据库连接就可以了,真正达到了一套系统的无缝切换。里面有些可能还说的不够准确,可能也还有遗漏的地方,仅供参考吧!!!
访问类库的文件完整的贴一次,如下:

代码如下:

//*****************************************************************************************************************
//* 编写人 :peace
//* EMAIL : peacechzh@126.com
//* 开发日期:2009-10-21
//* 修 改 人:
//* 修改日期:
//* 描 述:数据库工厂访问类
//* 更新描述:里面供调用执行的各方法可带参数执行,在外部指定参数名和参数值即可。
//* 最终期望:支持.NET所支持的所有数据库并达到系统的无缝切换(尽情的忽悠吧O(∩_∩)O~)
//*****************************************************************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Configuration;
using System.Reflection;
namespace DataProvider
{
public class DataProviderFactory
{
public DbConnection conn;//抽象类型
private DbCommand cmd;//抽象类型
private DbProviderFactory provider;
private DbParameter Para;//不同数据库参数类型的抽象类型
private DbDataAdapter Adapter;//对应不同数据库的数据适配器
Dictionary<Type, String> ParametersFormat;//不同数据库参数格式化类型
public string retParaformat = string.Empty;//最终返回的格式化标志,如@{0},:{0}
public DataProviderFactory()
{
//从配置文件中取出标示数据库类型的字符串并通过ProviderName的不同支持不同类型的数据库
string providerName = ConfigurationManager.ConnectionStrings["ConnStr"].ProviderName;//也可以用索引,从1开始
//创建一个数据库对应的实例,使用该实例就可以创建对应的connection,command 和adapater等等对象
provider = DbProviderFactories.GetFactory(providerName);
//创建具体的数据库连接类型和命令执行类型
conn = provider.CreateConnection();
conn.ConnectionString = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
cmd = provider.CreateCommand();
cmd.Connection = conn;
//创建具体的参数类型
Para = provider.CreateParameter();
//创建具体的适配器类型
Adapter = provider.CreateDataAdapter();
//不同数据库参数前缀格式化
ParametersFormat = new Dictionary<Type, String>();
ParametersFormat.Add(typeof(System.Data.SqlClient.SqlCommand), "@{0}");//因SQL SERVER只返回{0}没有@前缀,在此初始化处理
//返回格式化标志
retParaformat = GetParameterFormat(cmd);
}
/// <summary>
/// 添加参数
/// </summary>
/// <param name="ParaName">参数名称</param>
/// <param name="SqlType">参数数据类型</param>
/// <param name="ParaValue">参数值</param>
/// <param name="ParaCollect">参数对象的集合</param>
public void AddParam(string ParaName, DbType SqlType, object ParaValue, DbParameterCollection ParaCollect)
{
//不允许将一个DbCommand对象的Parameters插入到另外一个DbCommand对象,那么多个参数的话可以加上下面一句判断
//如果已经存在至少一个对象时,再深层拷贝一个
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat, ParaName);
Para.DbType = SqlType;
if (ParaValue == null)
{
Para.Value = string.Empty;//DBNull.Value;
}
else
{
Para.Value = ParaValue;
}
ParaCollect.Add(Para);
}
public void AddParam(string ParaName, object ParaValue, DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat, ParaName);//将参数格式化为具体的数据库参数格式
if (ParaValue == null)
{
Para.Value = string.Empty;
}
else
{
Para.Value = ParaValue;
}
ParaCollect.Add(Para);
}
/// <summary>
/// 存储过程输入参数
/// </summary>
/// <param name="ParaName"></param>
/// <param name="ParaValue"></param>
/// <param name="ParaCollect"></param>
public void AddInputParam(string ParaName, object ParaValue, DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat.Replace(":",""), ParaName);//ORACLE存储过程参数前没有冒号
if (ParaValue == null)
{
Para.Value = string.Empty;
}
else
{
Para.Value = ParaValue;
}
ParaCollect.Add(Para);
}
/// <summary>
/// 存储过程输出参数
/// </summary>
/// <param name="ParaName"></param>
/// <param name="ParaValue"></param>
/// <param name="ParaCollect"></param>
public void AddOutputParam(string ParaName, DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat.Replace(":", ""), ParaName);
Para.Value = string.Empty;
ParaCollect.Add(Para);
ParaCollect[Para.ParameterName].Direction = System.Data.ParameterDirection.Output;//指定该参数为输出参数
}
/// <summary>
/// 存储过程返回值参数
/// </summary>
/// <param name="ParaName"></param>
/// <param name="ParaValue"></param>
/// <param name="ParaCollect"></param>
public void AddReturnParam(string ParaName,DbParameterCollection ParaCollect)
{
if (ParaCollect.Count >= 1)
{
Para = (DbParameter)((ICloneable)ParaCollect[0]).Clone();
}
Para.ParameterName = string.Format(retParaformat.Replace(":", ""), ParaName);
Para.Value = string.Empty;
ParaCollect.Add(Para);
ParaCollect[Para.ParameterName].Direction = System.Data.ParameterDirection.ReturnValue;//指定该参数为返回值参数
}
/// <summary>
/// 抽象参数集合类型
/// </summary>
/// <returns></returns>
public DbParameterCollection GetParmCollection()
{
return cmd.Parameters;
}
/// <summary>
/// 执行SQL并返回数据集
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public DataSet ExecDataSet(string Sql)
{
DataSet ds = new DataSet();
try
{
this.Open();
cmd.CommandText = Replace(Sql);
Adapter.SelectCommand = cmd;
Adapter.Fill(ds);
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
return ds;
}
/// <summary>
/// 执行SQL语句并返回DataReader对象
/// </summary>
/// <param name="dbcon"></param>
/// <param name="cmdText"></param>
/// <returns></returns>
public DbDataReader ExecuteDataReader(DbConnection dbcon,string cmdText)
{
try
{
if (dbcon.State == ConnectionState.Closed)
{
dbcon.Open();
}
cmd.CommandText = Replace(cmdText);
DbDataReader dr = cmd.ExecuteReader();
cmd.Parameters.Clear();
cmd.Dispose();
return dr;
}
catch
{
dbcon.Close();//发生异常在此处关闭,否则在调用显式处关闭
return null;
}
}

/// <summary>
/// 判断记录是否存在
/// </summary>
/// <param name="Sql"></param>
/// <returns></returns>
public bool Exist(string Sql)
{
bool exist;
this.Open();
cmd.CommandText = Replace(Sql);
DbDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
exist = true; //记录存在
}
else
{
exist = false; //记录不存在
}
dr.Close();
this.Close();
return exist;
}
/// <summary>
/// 执行SQL语句
/// </summary>
/// <param name="sql"></param>
public void ExecSql(string Sql)
{
try
{
this.Open();
cmd.CommandText = Replace(Sql);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}

}
/// <summary>
/// 执行SQL语句,返回一个单值
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public string ReturnValue(string Sql)
{
object returnValue = string.Empty;
try
{
this.Open();
cmd.CommandText = Replace(Sql);
returnValue = cmd.ExecuteScalar();
if (returnValue == null)
{
returnValue = string.Empty;
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
return returnValue.ToString();
}
/// <summary>
/// 执行多条SQL语句并启用数据库事务
/// </summary>
/// <param name="SQLStringList"></param>
public bool ExecSqlTran(List<String> SQLStringList)
{
this.Open();
DbTransaction trans = conn.BeginTransaction();
cmd.Transaction = trans;
try
{
for (int n = 0; n < SQLStringList.Count; n++)
{
cmd.CommandText = Replace(SQLStringList[n]);
cmd.ExecuteNonQuery();
}
trans.Commit();
return true;
}
catch
{
trans.Rollback();
return false;
}
finally
{
this.Close();
}
}

/// <summary>
/// 执行存储过程并返回结果集
/// </summary>
/// <param name="storedProcName">存储过程名</param>
/// <returns>DataSet</returns>
public DataSet RunProcedure(string storedProcName)
{
DataSet ds = new DataSet();
try
{
this.Open();
cmd.CommandText = storedProcName;
cmd.CommandType = CommandType.StoredProcedure;
Adapter.SelectCommand = cmd;
//Adapter.SelectCommand.CommandTimeout = 1200;//可以设置适当的超时时间(秒),避免选择时间段过大导致填充数据集超时
Adapter.Fill(ds);
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
return ds;
}
/// <summary>
/// 执行存储过程,方法不返回结果集
/// </summary>
/// <param name="storedProcName"></param>
public void RunVoidProcedure(string storedProcName)
{
cmd.CommandText = storedProcName;
cmd.CommandType = CommandType.StoredProcedure;
try
{
this.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.Close();
}
}

/// <summary>
/// 将实体类的属性进行参数转换(ORACLE测试通不过,必须要求所有参数都包含在语句中才行)
/// </summary>
/// <param name="model"></param>
/// <param name="ParaCollect"></param>
//public void ConvertToParameters(object model, DbParameterCollection ParaCollect)
//{
// Type T = model.GetType();
// PropertyInfo[] propert = T.GetProperties();
// for (int i = 0; i < propert.Length; i++)
// {
// AddParam(propert[i].Name, propert[i].GetValue(model, null), ParaCollect);
// }
//}
/// <summary>
/// 将实体类的属性进行参数转换
/// </summary>
/// <param name="model"></param>
/// <param name="ParaCollect"></param>
public void ConvertToParameters(object model, DbParameterCollection ParaCollect,List<string> fields)
{
Type T = model.GetType();
PropertyInfo[] propert = T.GetProperties();
for (int i = 0; i < propert.Length; i++)
{
if (fields.Contains(propert[i].Name)) //检测必须参数化的实体属性
{
AddParam(propert[i].Name, propert[i].GetValue(model, null), ParaCollect);
}
}
}

/// <summary>
/// 通过反射将取出的数据写入实体类(ORACLE测试通不过,需进行类型强制转换)
/// </summary>
/// <param name="model"></param>
/// <param name="cmdText"></param>
//public void GetModel(object model, string cmdText)
//{
// PropertyInfo propertyInfo;
// DbDataReader dr = ExecuteDataReader(conn, cmdText);
// while (dr.Read())
// {
// for (int i = 0; i < dr.FieldCount; i++)
// {
// propertyInfo = model.GetType().GetProperty(dr.GetName(i));
// if (propertyInfo != null)
// {
// if (dr.GetValue(i) != DBNull.Value)
// {
// //Type t = dr.GetValue(i).GetType();
// propertyInfo.SetValue(model, dr.GetValue(i), null);
// }
// }
// }
// }
// dr.Close();
// conn.Close();
//}
/// <summary>
/// 通过反射将数据绑定到实体对象,由于不同数据库对应于.NET的数据类型不一样
/// 需做强制类型转换
/// </summary>
/// <param name="model"></param>
/// <param name="cmdText"></param>
public void GetModel(object model, string cmdText)
{
PropertyInfo propertyInfo;
DbDataReader dr = ExecuteDataReader(conn, cmdText);
object _value;
while (dr.Read())
{
for (int i = 0; i < dr.FieldCount; i++)
{
propertyInfo = model.GetType().GetProperty(dr.GetName(i));
if (propertyInfo != null && dr.GetValue(i) != DBNull.Value)
{
switch (propertyInfo.PropertyType.ToString())
{
case "System.String":
{
_value = Convert.ToString(dr.GetValue(i));//字符串是全球通用类型,也可以不用转换
propertyInfo.SetValue(model, _value, null);
}break;
case "System.Int32":
{
_value = Convert.ToInt32(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "System.Single":
{
_value = Convert.ToSingle(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "System.Decimal":
{
_value = Convert.ToDecimal(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "System.Double":
{
_value = Convert.ToDouble(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
case "":
{
_value = Convert.ToDateTime(dr.GetValue(i));
propertyInfo.SetValue(model, _value, null);
} break;
default: break;
}
}
}
}
dr.Close();
conn.Close();
}
/// <summary>
/// 根据不同的数据库命令对象返回该类型数据库参数的前缀格式化字符串
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
private string GetParameterFormat(DbCommand command)
{
if (!ParametersFormat.ContainsKey(command.GetType()))
{
this.Open();//读取参数前缀时需打开数据库连接
ParametersFormat.Add(
command.GetType(),
command.Connection.GetSchema("DataSourceInformation")
.Rows[0]["ParameterMarkerFormat"].ToString());
//conn.Close();在真正执行语句的时候去关闭,避免重复打开
}
return ParametersFormat[command.GetType()];
}
private void Open()
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
}
private void Close()
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
}
/// <summary>
/// 替换DML语句里的参数前缀
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public string Replace(string str)
{
return str.Replace("$", retParaformat.Substring(0, 1));
}
}
}

文件下载:PeaceHelper.cs

(0)

相关推荐

  • ADO.NET 的最佳实践技巧

    这是我很早以前看过的微软的一篇文章,最近,一些网友问的问题很多理论都在里面,所以,整理一下放在这里,大家可以参考一下. 简介 本文为您提供了在 Microsoft ADO.NET 应用程序中实现和获得最佳性能.可伸缩性以及功能的最佳解决方案:同时也讲述了使用 ADO.NET 中可用对象的最佳实践:并提出一些有助于优化 ADO.NET 应用程序设计的建议. 本文包含: • 有关 .NET 框架包含的 .NET 框架数据提供程序的信息. • DataSet 和 DataReader 之间的比较,以及

  • ADO.NET EF中的实体修改方法

    1.传统修改模式,看下列代码 复制代码 代码如下: using (NorthwindEntities context = new NorthwindEntities()) { Region region = context.Region.FirstOrDefault(v => v.RegionID == 4); region.RegionDescription = "Test"; context.SaveChanges(); } 监控SQL语句: 复制代码 代码如下: SQL1:

  • 数据库开发总结(ADO.NET小结)

    一.用SqlConnection连接SQL Server 1..加入命名空间 using System.Data.SqlClient; 2.连接数据库 SqlConnection myConnection = new SqlConnection(); myConnection.ConnectionString = "user id=sa;password=sinofindb;initial catalog=test;data source=127.0.0.1;Connect Timeout=30

  • 当年学习ADO.NET的笔记

    那些年我还在学ADO.NET 那些年学习了ASP.NET后又开始学习ASP.NET的新知识,ADO.NET用于访问数据库,一般可以分为连接模式和非连接模式.连接模式指的是在访问数据时,一直与数据库保持连接,访问完数据后才与数据库断开连接,主要采用的ADO.NET对象是Connection.Command.DataReader等:连接模式指的是通过数据集的方式对数据库进行操作,将数据读到内存中,从而完成数据的操作,数据集会自动更新到数据库,主要采用ADO.NET对象是DataAdapter.Dat

  • ADO.NET实用技巧两则

    要想充分发挥ADO.NET的优势,不仅需要全面.深入理解ADO.NET编程模型,及时总结经验.技巧也十分重要.ADO已经有多年的实践经验,ADO.NET以此为基础,提供了更加丰富.强大的工具:尽管如此,ADO.NET的设计目标毕竟不是提供一个即插即用的工具,它不会把所有的编程工作简化到仅靠鼠标点击就可以完成的程度.  ADO.NET包含了一大堆代表数据访问模型中各种逻辑实体的对象,其中尤以连接.事务这两个对象最为重要.连接的作用是建立一个与后端数据库通信的通道,创建连接对象必须以特定的.NET数

  • 用C#对ADO.NET数据库完成简单操作的方法

    数据库访问是程序中应用最普遍的部分.随着C#和ADO.NET的引入,这种操作变得更简单.这篇文章将示范四种最基础的数据库操作.  ● 读取数据.其中包括多种数据类型:整型,字符串,日期型.  ● 写数据.如读数据一样,我们也需要写入多种类型的数据.这可以通过SQL语句来完成.  ● 更新或修改数据.我们将再次用到SQL语句.  ● 删除数据.用SQL实现.  上述的操作都要基于Microsoft Access 2000数据库,但是,我们要对连接字符串进行简单的修改才能使用SQL或其他ADO数据.

  • ASP.NET:ADO.NET的DataAdapter对象

    DataAdapter对象可以隐藏和Connection.Command对象沟通的细节,通过DataAdapter对象建立.初始化DataTable,从而和DataSet对象结合起来在内存存放数据表副本,实现离线式数据库操作,事实上在8.1.2节中,我们在介绍DataSet数据模型的时候就已经演示了如何利用DataAdapter对象进行数据库连接.DataAdapter对象允许将DataSet对象中的数据保存到数据源中,也可以从数据源中读取数据,并且也可以底层数据保存体进行数据的添加.删除.更新

  • 一个经典的ADO.NET入门例子

    众所周知,ADO.NET相对于ADO的最大优势在于对于数据的更新修改可以在与数据源完全断开联系的情况下进行,然后再把数据更新情况传回到 数据源.这样大大减少了连接过多对于数据库服务器资源的占用.下面是我在<ADO.NET实用指南>这本书上看到的一个例子,比较清楚的讲解 了ADO.NET的使用方法.  Imports System.Data.SqlClient  Imports System.Data  Imports System.Data.Common Public Class Form1 

  • 告别ADO.NET实现应用系统无缝切换的烦恼(总结篇)

    可能只是那么想也是那么设计的,要支持多数据库,要能支持多数据库,万一要是以后数据库变了怎么办?万一要是...怎么办?这些顾虑很多时候是不必要的,反而绕了弯子.大都是做项目应用系统而非产品,即使要用不同的数据库了,基本上是吧上一个项目全COPY过来,修修改改OK了.产品可能就不一样了,那才可能要支持真正的多数据库,才可能会面对真正的数据库访问类库的多数据库的实际检验.ADO.NET2.0下增强了数据库访问的功能,也就是工厂式类库,提到工厂式数据库访问,网上可就多了,ADO.NET2.0增强的工厂式

  • jquery实现LED广告牌旋转系统图片切换效果代码分享

    本文实例讲述了jquery实现LED广告牌旋转系统图片切换效果,分享给大家供大家参考.具体如下: js模拟路边巨大显示屏上广告切换效果,不得不相信js做到了,而且让你无话可说的逼真效果. LED广告显示器上图片切换效果,场景也类似,效果相当震撼看得我目瞪口呆,热爱特效的你可不要错过哈! 运行效果图: -------------------查看效果 下载源码------------------- 小提示:浏览器中如果不能正常运行,可以尝试切换浏览模式. 为大家分享的jquery实现LED广告牌旋转

  • Android 系统语言切换监听和设置实例代码

    最近项目上产品经理提了个需求,要求关闭语言国际化,不管手机系统设置那个国家的语言,都要显示汉语,好吧,既然有需求,那就做吧.但是项目中已经有英文的配置了,且是作为默认String提供的,这么多翻译好的文字,直接删除掉替换成中文为默认String又感觉弃之可惜.故网上Google下解决方案.就开始往下看吧. 一.代码中动态设置应用显示语言(手动控制使用values-zh-rCN下字符串) 这个方法是通过改变Resource中的配置来实现的,代码如下: public static void init

  • Android利用Hero实现列表与详情页无缝切换动画

    目录 前言 思路 列表元素 详情页面 源码 总结 前言 介绍了几篇 Hero 动画,我们来一个 Hero 动画应用案例.在一些应用中,列表的元素和详情的内容是一致的,这个时候利用 Hero 动画切换到详情会感觉无缝过渡,用户体验会更好.例如本篇我们要实现下面的效果: Hero 应用:列表与详情切换 思路 上面的效果是列表和详情共用了头像和头像的背景色.二者的组合是一个 Stack 组件,因此可以使用 Hero 组件完成.然后是 Hero 组件的移动,我们先做了水平移动,再做垂直方向移动,这样过渡

  • ubuntu系统下切换python版本的方法

    目录 一.概述 二.使用update-alternatives更新python版本 2.1.查看ubuntu下的所有python版本 2.2.更新update-alternatives替代列表 2.3.查看update-alternatives下的python版本 2.4.切换python版本 2.5.删除python版本 总结 一.概述 在ubuntu环境下进行嵌入式开发,我们在进行不同的项目开发时,可能会遇到python环境不统一的情况.这时,我们可以通过update-alternative

  • Android实现系统语言切换功能

    简单介绍下这个需求的缘由,这段时间因公司业务需要,其中有一项"设置系统语言"功能,就是在使用APP的过程中,动态的去切换整个Android机器的语言,具体参照手机设置页面有语言切换功能.起初想来是很简单的事情嘛,不就是个简单的资源国际化嘛,strings.xml资源文件一整还不给OK?真正动起手来就真不是这么一回事了,国际化是没问题,但是怎样能更改所有页面的文字资源呢,这是一个问题.下面介绍下网上找的几个方案. 一.API欺骗 烧制到手机中的android.jar包含了Android所

  • Android使用MediaPlayer和TextureView实现视频无缝切换

    需求描述 比如广告播放,每个视频15秒,视频之间切换的时候,性能不太好的机器可能会黑屏一段时间,体验不太好,接下来我们就是要解决这个黑屏问题. 解决方案 使用两个surfaceView方式,经过测试不行 使用一个MediaPlayer,在MediaPlayer上面加一层ImageView,每次播放完成后,获取视频的最后一帧的图像给ImageView,视频切换完成,ImageView隐藏,如此往复循环,可行 实践 1.获取视频流图片方式,通过MediaMetadataRetriever,测试发现,

  • jcl与jul log4j1 log4j2 logback日志系统机制及集成原理

    目录 apache commons-logging 1 简单的使用案例 2 使用原理 1 获取LogFactory的过程 2 根据LogFactory获取Log的过程 commons-logging与jul集成 1 需要的jar包 2 使用案例 3 使用案例分析 1 获取获取LogFactory的过程 2 根据LogFactory获取Log的过程 commons-logging与log4j1集成 1 需要的jar包 4.2 使用案例 3 使用案例分析 1 获取获取LogFactory的过程 2

  • slf4j jcl jul log4j1 log4j2 logback各组件系统日志切换

    目录 各种jar包总结 slf4j转向某个实际的日志框架: 某个实际的日志框架转向slf4j: 集成总结 commons-logging与其他日志框架集成 slf4j与其他日志框架集成 日志系统之间的切换 log4j无缝切换到logback 1 案例 2 切换原理 jdk-logging无缝切换到logback 1 案例 切换原理 commons-logging切换到logback 使用案例 切换原理 常用的日志场景切换解释 1 左上图 2 右上图 3 左下图 冲突说明 jcl-over-slf

  • 新手进阶 透彻认识64位操作系统

    现在,Windows XP 64 Professional x64 Edition(以下简称Windows XP 64)已经正式推出,和32位的Windows XP相比,它有哪些显著的变化呢?它能顺利支持32位应用吗?普通用户现在可以升级到Windows XP 64吗? 跨入64位之路 "位",简单地理解就是CPU的寻址空间.在计算机技术的发展史上,位的演变起到了举足轻重的作用.从1971年的4位计算到如今已来临的64位计算时代,经历了漫长的演变过程. 1978--1995年:16位大

随机推荐