时间:2021-07-01 10:21:17 帮助过:20人阅读
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方法如下:
代码如下:输出参数可以有多个,也就可以根据需要能有多个所谓的“返回值”,所以我之前的开发中一直是用output参数来代替return参数。
/// <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();
}
}
下面说下两个存储过程,存储过程基本上分两种,返回结果集的存储过程和执行业务逻辑不返回结果集但却有返回值(如标志等),对于需要有返回值的存储过程,我个人趋向于用
参数代替返回值,因为都能达到一样的效果目的,而且
代码如下:
/// <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();
}
}
下面说两个反射方法,测试之后为了方便调用,减少操作添加的,一个是把实体类的属性转换为参数,另一个是把从数据库取出的某条记录转换为实体类,这两个还是非常有用,尤其是在系统开发时调用比较方便,以前我是见到反射就绕道走的,这次算是第一次用反射,发现确实是很方便。如下:
代码如下:输出参数值和返回值的测试 SQLSERVER和ORACLE都通过
/// <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所支持的所有书库都测试一遍,那测试量可不小了,呵呵。
代码如下:输出参数值和返回值,方法不返回结果集 SQLSERVER通过
<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];
//带参数测试存储过程的
//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型
//改进后带参数测试存储过程的
//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