2009-12-07至10这四天,一直在完善这个通用的封装数据库操作的类,编写程序边写下心得,整理如下:
1. 先说:Serializable
如果程序实例需要保存到Session的话,必须在实例类的头部加上[Serializable],否则无法编译通过。如:
[Serializable]
public class Order
{
...
}
详细信息请参考:
http://blog.csdn.net/bentonite/archive/2009/10/10/4648830.aspx
2. 构造函数
如果不添加构造函数,编译器会自动创建一个无参数的构造函数。
可创建多个带不同参数的构造函数:
public Order(string propertyName, bool ascending)
{
this.propertyName = propertyName;
this.ascending = ascending;
}
另外,创佳构造函数时可以使用如下方法调用该类的另外一个构造函数或父类的构造函数:
调用该类其他构造函数:
public QueryStringBuilder() {}
public QueryStringBuilder(string tableName, string primaryKeyName)
: this()
{
...
}
调用父类构造函数:
public class QueryBase
{
public QueryBase() {}
}
public class QueryStringBuilder : QueryBase
{
public QueryStringBuilder(string tableName, string primaryKeyName)
: base()
{
...
}
}
注意:只能同时调用一个其他的构造函数
3. 复写ToString()方法:
public override string ToString()
{
return "";
}
4. 创建范型类:
public class QueryStringBuilder<T, TKey> : IQuery<T, TKey>
使用:
QueryStringBuilder<string,int> instance = new QueryStringBuilder<string,int>();
注意,这里的T,TKey表示范型,可变类型,可以是基本类型或自定义类
5. 链型操作
链型操作使用举例:
private IQuery<ContentArticle, int> query = new QueryStringBuilder<ContentArticle,int>("Content_Article", "GeneralID");
List<ContentArticle> actualPaged = query.SetFirstResult(-10).SetMaxResult(8).List();
这个就是:query.SetFirstResult(-10).SetMaxResult(8).List();
实现:
public IQuery<T,TKey> SetFirstResult(int firstResult)
{
this.firstResult = firstResult;
return this;
}
public IQuery<T,TKey> SetMaxResult(int maxResult)
{
this.maxResult = maxResult;
return this;
}
其中:return this; 返回自身实例,那就可以继续其他操作。
注意:此法不便于调试。
6. SQLHelper用法:
SqlDataReader rdr = SqlHelper.ExecuteReader(connectionString, CommandType.StoredProcedure, SP_GetPagedList, param)
SqlDataReader rdr = SqlHelper.ExecuteReader(connectionString, CommandType.Text, commandText, null)
int count = (int)SqlHelper.ExecuteScalar(connectionString, CommandType.StoredProcedure, SP_GetCount, param);
SqlHelper.ExecuteNonQuery(connectionString, CommandType.Text, commandText, param.ToArray())
方法 ExecuteReader() 返回数据记录集合,可使用SQL文本命令或存储过程,可接受对应SQL文本命令或存储过程的参数,适合于请求记录/集
方法 ExecuteScalar() 返回第一条符合条件的记录的第一个字段,适合于请求记录数
方法 ExecuteNonQuery() 不返回值,适合以创建、更新、删除(CUD)操作
7. Sql参数使用
单个参数:
SqlParameter param = new SqlParameter("@TableName",SqlDbType.NVarChar,500);
param.Value = "ContentArticle";
如果不清楚参数类型,有更方便的重载函数:
SqlParameter param = new SqlParameter("@TableName",“ ContentArticle”);
如果参数用于输出(添加记录后返回刚添加的ID),需要设置属性:
param .Direction = ParameterDirection.Output;
多参数:
方式1,适合具有固定数量的参数:
SqlParameter[] param = new SqlParameter[]
{
new SqlParameter("@TableName",SqlDbType.NVarChar,500),
new SqlParameter("@PrimaryKey",SqlDbType.NVarChar,20),
new SqlParameter("@StrWhere",SqlDbType.NVarChar,2000),
};
param[0].Value = tableName;
param[1].Value = primaryKeyName;
param[2].Value = selectString;
方式2,参数数量不定:
List<SqlParameter> param = new List<SqlParameter>();
foreach (PropertyInfo prop in propInfo)
{
SqlParameter par = null;
if (prop.Name == primaryKeyName)
{
par = new SqlParameter("@" + prop.Name,SqlDbType.Int,4);
par.Direction = ParameterDirection.Output;
}
else
{
par = new SqlParameter();
par.ParameterName = "@" + prop.Name;
par.Value= prop.GetValue(entity, null);
}
param.Add(par);
}
8. try...catch...的使用
try
{
int i = SqlHelper.ExecuteNonQuery(SqlHelper.ConnectionStringLocalTransaction, CommandType.Text, commandText, param.ToArray());
if (i > 0)
{
returnObject = entity;
primaryProp.SetValue(returnObject, param[0].Value, null);
}
}
catch (Exception ex)
{
LogUtil.error(ex.Message);
throw;
}
安全操作在try块内,但出现异常时,自动跳至catch块处理异常信息。
Exception 是抛出的异常,使用LogUtil.error(ex.Message);可即时记录异常信息。
此处不再做更多的异常操作,所以再次抛出去,交由其他方法处理。
9. using 的使用
using (SqlDataReader rdr = SqlHelper.ExecuteReader(SqlHelper.ConnectionStringLocalTransaction, CommandType.StoredProcedure, SP_GetPagedList, param))
{
while (rdr.Read())
{
data = (T)LoadData(rdr, typeof(T));
list.Add(data);
}
}
为了确保using块里的实例资源即时释放,等价于:
SqlDataReader rdr = SqlHelper.ExecuteReader(SqlHelper.ConnectionStringLocalTransaction, CommandType.StoredProcedure, SP_GetPagedList, param)
try
{
while (rdr.Read())
{
data = (T)LoadData(rdr, typeof(T));
list.Add(data);
}
}
catch
{
...
}
finally
{
rdr.Dispose();
}
10. ref 引用
相当于指针,通过内存地址直接操作变量,而不是通过拷贝传值
定义和调用据需要关键字ref
private void LoadData(IDataRecord dr, ref ContentTemplateProject data){}
LoadData(rdr, ref data);
11.事务
1).创建数据库连接
2).打开数据库
3).创建事务
4).try 块内执行所有操作后,统一提交
5).出现异常,事务回滚
6).日志记录
7).抛出异常
8).释放数据库连接
SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
SqlTransaction trans = conn.BeginTransaction("DELETELIST");
try
{
...
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback("DELETELIST");
LogUtil.error(ex.Message);
throw;
}
finally
{
conn.Close();
}
12. 反射(Reflection):
object querySecond = new QueryStringBuilder<ContentNode, int>("Content_Node", "NodeID");
1)获取实例类型信息
方法1:typeof(querySecond )
方法2:querySecond. GetType()
2) 获取实例的变量及其值:
string rightPrimaryKeyName = querySecond.GetType().GetField("primaryKeyName").GetValue(querySecond).ToString();
GetType() 获取动态运行库里的实例类型信息,包括构造函数、属性、方法、变量
GetField(string 变量名) 获取实例的指定变量名的变量信息
GetValue(object 实例) 从实例获取指定变量名的值
3)创建动态类型的实例
private object LoadData(SqlDataReader rdr, Type type)
{
object returnObject = Assembly.GetAssembly(type).CreateInstance(type.ToString());
}
List<ContentNode> nodes = new List<ContentNode>();
dataRight = (ContentNode)LoadData(rdr, typeof(ContentNode));
nodes.Add(dataRight );
函数定义使用Type,函数使用通过typeof传入Type参数
静态方法 Assembly.GetAssembly(Type 实例类型)加载该类型的程序集信息
CreateInstance(string 实例类型名称)创建该类型的实例
4.获取实例属性
PropertyInfo[] props = returnObject.GetType().GetProperties();
方法GetProperties()获取该实例的全部属性
属性读取:
prop.GetValue(returnObject, null)
可获取实例returnObject的指定属性prop的值
属性写:
prop.SetValue(returnObject, rdr[prop.Name], null);
为实例returnObject的指定属性prop的赋值
切记prop必须是从returnObject实例中取出来的
迭代属性并动态写入
foreach (PropertyInfo prop in props)
{
prop.SetValue(returnObject, rdr[prop.Name], null);
}
13.接口实现
声明接口:public interface IQuery<T, TKey>
继承接口:public class QueryStringBuilder<T, TKey> : IQuery<T, TKey>
14. NUnit单元测试
1). 引用类库:using NUnit.Framework;
2). 测试类必须加标识 [TestFixture]:
[TestFixture]
class QueryStringBuilderTest
{
...
}
3). 测试类首先执行具有此属性的方法: [TestFixtureSetUp]
[TestFixtureSetUp]
public void Constructor(){}
4). 最后执行具有此属性的方法:[TestFixtureTearDown]
[TestFixtureTearDown]
public void Destructor(){}
5). 每个测试方法必须具有属性:[Test]
[Test]
public void ToStringTest(){}
6). 基本断言:
string expected = "SELECT * FROM Node";
string actual = query.ToString();
Assert.That(actual, Is.EqualTo(expected), "结果不相等!");