一般来说,对系统的分层,一般都需要下面几个层:实体层(Entity)、数据访问层(DAL)、业务逻辑层(BLL)、界面层(UI);而数据访问层,一般也会加入一个接口层(IDAL)。
在其中的实体层,一般是根据数据库进行映射外加入注释等,技术含量不大,在此一笔带过;数据库访问层和业务逻辑层,是关键之所在,因为这里好的设计,会利用很多基类的操作,减少很多代码和重复劳动;界面层,不管是WebForm还是WinForm,都是尽可能少的逻辑代码或者SQL语句在其中,好的项目可能会利用一些优秀的控件进去,提高体验,减少代码。另外,由于一些创建操作费时费资源,一般还需要把可重复利用的资源缓存起来,提高性能。
先给大家预览下项目的框架,再一层层分析讨论:
1、 实体层(定义一个空的基类,其他实体类继承之,主要是为了利用泛型操作,用途下面细说)
public class BaseEntity
{
}
public class EquipmentInfo : BaseEntity
{
Field Members#region Field Members
private int m_ID = 0; //ID
private string m_PartID = ""; //备件编号
//
#endregion
Property Members#region Property Members
/**//// <summary>
/// ID
/// </summary>
public virtual int ID
{
get
{
return this.m_ID;
}
set
{
this.m_ID = value;
}
}
/**//// <summary>
/// 备件编号
/// </summary>
public virtual string PartID
{
get
{
return this.m_PartID;
}
set
{
this.m_PartID = value;
}
}
//
#endregion
}
2、 数据库访问层,数据访问层的关键是数据访问基类的设计,基类实现大多数数据库的日常操作,如下:
/**//// <summary>
/// 数据访问层的基类
/// </summary>
public abstract class BaseDAL<T> : IBaseDAL<T> where T : BaseEntity, new()
{
}
BaseEntity就是实体类的基类,IBaseDAL是定义的数据访问基类接口,包含各种常用的操作定义;因此BaseDAL就是要对各种操作的进行实现,实现接口越多,将来继承类的重用程度就越高。
以上通过泛型<T> ,我们就可以知道实例化那个具体访问类的信息了,可以实现强类型的函数定义。
/**//// <summary>
/// 一些基本的,作为辅助函数的接口
/// </summary>
public interface IBaseDAL<T> where T : BaseEntity
{
/**//// <summary>
/// 查询数据库,检查是否存在指定键值的对象
/// </summary>
/// <param name="recordTable">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns>
bool IsExistKey(Hashtable recordTable);
/**//// <summary>
/// 查询数据库,检查是否存在指定键值的对象
/// </summary>
/// <param name="fieldName">指定的属性名</param>
/// <param name="key">指定的值</param>
/// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns>
bool IsExistKey(string fieldName, object key);
/**//// <summary>
/// 获取数据库中该对象的最大ID值
/// </summary>
/// <returns>最大ID值</returns>
int GetMaxID();
/**//// <summary>
/// 根据指定对象的ID,从数据库中删除指定对象
/// </summary>
/// <param name="key">指定对象的ID</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
bool DeleteByKey(string key);
/**//// <summary>
/// 根据条件,从数据库中删除指定对象
/// </summary>
/// <param name="condition">删除记录的条件语句</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
bool DeleteByCondition(string condition);
/**//// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <returns>执行成功返回True</returns>
bool Insert(T obj);
/**//// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
bool Update(T obj, string primaryKeyValue);
/**//// <summary>
/// 查询数据库,检查是否存在指定ID的对象(用于整型主键)
/// </summary>
/// <param name="key">对象的ID值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
T FindByID(int key);
/**//// <summary>
/// 查询数据库,检查是否存在指定ID的对象(用于字符型主键)
/// </summary>
/// <param name="key">对象的ID值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
T FindByID(string key);
返回集合的接口#region 返回集合的接口
/**//// <summary>
/// 根据ID字符串(逗号分隔)获取对象列表
/// </summary>
/// <param name="idString">ID字符串(逗号分隔)</param>
/// <returns>符合条件的对象列表</returns>
List<T> FindByIDs(string idString);
/**//// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="condition">查询的条件</param>
/// <returns>指定对象的集合</returns>
List<T> Find(string condition);
/**//// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="condition">查询的条件</param>
/// <param name="info">分页实体</param>
/// <returns>指定对象的集合</returns>
List<T> Find(string condition, PagerInfo info);
/**//// <summary>
/// 返回数据库所有的对象集合
/// </summary>
/// <returns>指定对象的集合</returns>
List<T> GetAll();
/**//// <summary>
/// 返回数据库所有的对象集合(用于分页数据显示)
/// </summary>
/// <param name="info">分页实体信息</param>
/// <returns>指定对象的集合</returns>
List<T> GetAll(PagerInfo info);
DataSet GetAllToDataSet(PagerInfo info);
#endregion
}
细看上面代码,会发现由一个PagerInfo 的类,这个类是用来做分页参数传递作用的,根据这个参数,你可以知道具体返回那些关心的记录信息,这些记录又转换为强类型的List<T>集合。
再看看数据库访问基类的具体实现代码吧:
/**//// <summary>
/// 数据访问层的基类
/// </summary>
public abstract class BaseDAL<T> : IBaseDAL<T> where T : BaseEntity, new()
{
构造函数#region 构造函数
protected string tableName;//需要初始化的对象表名
protected string primaryKey;//数据库的主键字段名
protected string sortField = "LastUpdated";//排序字段
private bool isDescending = true;//
/**//// <summary>
/// 排序字段
/// </summary>
public string SortField
{
get
{
return sortField;
}
set
{
sortField = value;
}
}
/**//// <summary>
/// 是否为降序
/// </summary>
public bool IsDescending
{
get { return isDescending; }
set { isDescending = value; }
}
/**//// <summary>
/// 数据库访问对象的表名
/// </summary>
public string TableName
{
get
{
return tableName;
}
}
/**//// <summary>
/// 数据库访问对象的外键约束
/// </summary>
public string PrimaryKey
{
get
{
return primaryKey;
}
}
public BaseDAL()
{}
/**//// <summary>
/// 指定表名以及主键,对基类进构造
/// </summary>
/// <param name="tableName">表名</param>
/// <param name="primaryKey">表主键</param>
public BaseDAL(string tableName, string primaryKey)
{
this.tableName = tableName;
this.primaryKey = primaryKey;
}
#endregion
通用操作方法#region 通用操作方法
/**//// <summary>
/// 添加记录
/// </summary>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public bool Insert(Hashtable recordField, DbTransaction trans)
{
return this.Insert(recordField, tableName, trans);
}
/**//// <summary>
/// 添加记录
/// </summary>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="targetTable">需要操作的目标表名称</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public bool Insert(Hashtable recordField, string targetTable, DbTransaction trans)
{
bool result = false;
string fields = ""; // 字段名
string vals = ""; // 字段值
if ( recordField == null || recordField.Count < 1 )
{
return result;
}
SqlParameter[] param = new SqlParameter[recordField.Count];
IEnumerator eKeys = recordField.Keys.GetEnumerator();
int i = 0;
while ( eKeys.MoveNext() )
{
string field = eKeys.Current.ToString();
fields += field + ",";
vals += string.Format("@{0},", field);
object val = recordField[eKeys.Current.ToString()];
param[i] = new SqlParameter("@" + field, val);
i++;
}
fields = fields.Trim(',');//除去前后的逗号
vals = vals.Trim(',');//除去前后的逗号
string sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2})", targetTable, fields, vals);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
command.Parameters.AddRange(param);
if ( trans != null )
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
/**//// <summary>
/// 更新某个表一条记录(只适用于用单键,用int类型作键值的表)
/// </summary>
/// <param name="id">ID号</param>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public bool Update(int id, Hashtable recordField, DbTransaction trans)
{
return this.Update(id, recordField, tableName, trans);
}
/**//// <summary>
/// 更新某个表一条记录(只适用于用单键,用string类型作键值的表)
/// </summary>
/// <param name="id">ID号</param>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public bool Update(string id, Hashtable recordField, DbTransaction trans)
{
return this.Update(id, recordField, tableName, trans);
}
/**//// <summary>
/// 更新某个表一条记录(只适用于用单键,用int类型作键值的表)
/// </summary>
/// <param name="id">ID号</param>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="targetTable">需要操作的目标表名称</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public bool Update(int id, Hashtable recordField, string targetTable, DbTransaction trans)
{
return Update(id, recordField, targetTable, trans);
}
/**//// <summary>
/// 更新某个表一条记录(只适用于用单键,用string类型作键值的表)
/// </summary>
/// <param name="id">ID号</param>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="targetTable">需要操作的目标表名称</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public bool Update(string id, Hashtable recordField, string targetTable, DbTransaction trans)
{
string field = ""; // 字段名
object val = null; // 值
string setValue = ""; // 更新Set () 中的语句
if ( recordField == null || recordField.Count < 1 )
{
return false;
}
SqlParameter[] param = new SqlParameter[recordField.Count];
int i = 0;
IEnumerator eKeys = recordField.Keys.GetEnumerator();
while ( eKeys.MoveNext() )
{
field = eKeys.Current.ToString();
val = recordField[eKeys.Current.ToString()];
setValue += string.Format("{0} = @{0},", field);
param[i] = new SqlParameter(string.Format("@{0}", field), val);
i++;
}
string sql = string.Format("UPDATE {0} SET {1} WHERE {2} = '{3}' ", targetTable, setValue.Substring
(0, setValue.Length - 1), primaryKey, id);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
command.Parameters.AddRange(param);
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
#endregion
对象添加、修改、查询接口#region 对象添加、修改、查询接口
/**//// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <returns>执行成功返回新增记录的自增长ID。</returns>
public bool Insert(T obj)
{
ArgumentValidation.CheckForNullReference(obj, "传入的对象obj为空");
Hashtable hash = GetHashByEntity(obj);
return Insert(hash, null);
}
/**//// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public bool Update(T obj, string primaryKeyValue)
{
ArgumentValidation.CheckForNullReference(obj, "传入的对象obj为空");
Hashtable hash = GetHashByEntity(obj);
return Update(primaryKeyValue, hash, null);
}
/**//// <summary>
/// 查询数据库,检查是否存在指定ID的对象(用于整型主键)
/// </summary>
/// <param name="key">对象的ID值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
public T FindByID(int key)
{
return FindByID(key.ToString());
}
/**//// <summary>
/// 查询数据库,检查是否存在指定ID的对象(用于字符型主键)
/// </summary>
/// <param name="key">对象的ID值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
public T FindByID(string key)
{
string sql = string.Format("Select * From dbo.{0} Where ({1} = @ID)", tableName, primaryKey);
SqlParameter param = new SqlParameter("@ID", key);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
command.Parameters.Add(param);
T entity = null;
using (IDataReader dr = db.ExecuteReader(command))
{
if (dr.Read())
{
entity = DataReaderToEntity(dr);
}
}
return entity;
}
#endregion
返回集合的接口#region 返回集合的接口
/**//// <summary>
/// 根据ID字符串(逗号分隔)获取对象列表
/// </summary>
/// <param name="idString">ID字符串(逗号分隔)</param>
/// <returns>符合条件的对象列表</returns>
public List<T> FindByIDs(string idString)
{
string condition = string.Format("{0} in({1})", primaryKey, idString);
return this.Find(condition);
}
/**//// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="condition">查询的条件</param>
/// <returns>指定对象的集合</returns>
public List<T> Find(string condition)
{
//串连条件语句为一个完整的Sql语句
string sql = string.Format("Select * From dbo.{0} Where ", tableName);
sql += condition;
sql += string.Format(" Order by {0} {1}", sortField, isDescending ? "DESC" : "ASC");
T entity = null;
List<T> list = new List<T>();
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
using (IDataReader dr = db.ExecuteReader(command))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
list.Add(entity);
}
}
return list;
}
/**//// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="condition">查询的条件</param>
/// <param name="info">分页实体</param>
/// <returns>指定对象的集合</returns>
public List<T> Find(string condition, PagerInfo info)
{
List<T> list = new List<T>();
Database db = DatabaseFactory.CreateDatabase();
PagerHelper helper = new PagerHelper(tableName, condition);
info.RecordCount = helper.GetCount();
PagerHelper helper2 = new PagerHelper(tableName, false, " * ", sortField,
info.PageSize, info.CurrenetPageIndex, true, condition);
using (IDataReader dr = helper2.GetDataReader())
{
while (dr.Read())
{
list.Add(this.DataReaderToEntity(dr));
}
}
return list;
}
/**//// <summary>
/// 返回数据库所有的对象集合
/// </summary>
/// <returns>指定对象的集合</returns>
public List<T> GetAll()
{
string sql = string.Format("Select * From dbo.{0}", tableName);
sql += string.Format(" Order by {0} {1}", sortField, isDescending ? "DESC" : "ASC");
T entity = null;
List<T> list = new List<T>();
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
using (IDataReader dr = db.ExecuteReader(command))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
list.Add(entity);
}
}
return list;
}
/**//// <summary>
/// 返回数据库所有的对象集合(用于分页数据显示)
/// </summary>
/// <param name="info">分页实体信息</param>
/// <returns>指定对象的集合</returns>
public List<T> GetAll(PagerInfo info)
{
List<T> list = new List<T>();
string condition = "";
Database db = DatabaseFactory.CreateDatabase();
PagerHelper helper = new PagerHelper(tableName, condition);
info.RecordCount = helper.GetCount();
PagerHelper helper2 = new PagerHelper(tableName, false, " * ", sortField,
info.PageSize, info.CurrenetPageIndex, true, condition);
using (IDataReader dr = helper2.GetDataReader())
{
while (dr.Read())
{
list.Add(this.DataReaderToEntity(dr));
}
}
return list;
}
public DataSet GetAllToDataSet(PagerInfo info)
{
DataSet ds = new DataSet();
string condition = "";
PagerHelper helper = new PagerHelper(tableName, condition);
info.RecordCount = helper.GetCount();
PagerHelper helper2 = new PagerHelper(tableName, false, " * ", sortField,
info.PageSize, info.CurrenetPageIndex, true, condition);
return helper2.GetDataSet();
}
#endregion
子类必须实现的函数(用于更新或者插入)#region 子类必须实现的函数(用于更新或者插入)
/**//// <summary>
/// 将DataReader的属性值转化为实体类的属性值,返回实体类
/// (提供了默认的反射机制获取信息,为了提高性能,建议重写该函数)
/// </summary>
/// <param name="dr">有效的DataReader对象</param>
/// <returns>实体类对象</returns>
protected virtual T DataReaderToEntity(IDataReader dr)
{
T obj = new T();
PropertyInfo[] pis = obj.GetType().GetProperties();
foreach (PropertyInfo pi in pis)
{
try
{
if (dr[pi.Name].ToString() != "")
{
pi.SetValue(obj, dr[pi.Name] ?? "", null);
}
}
catch { }
}
return obj;
}
/**//// <summary>
/// 将实体对象的属性值转化为Hashtable对应的键值(用于插入或者更新操作)
/// (提供了默认的反射机制获取信息,为了提高性能,建议重写该函数)
/// </summary>
/// <param name="obj">有效的实体对象</param>
/// <returns>包含键值映射的Hashtable</returns>
protected virtual Hashtable GetHashByEntity(T obj)
{
Hashtable ht = new Hashtable();
PropertyInfo[] pis = obj.GetType().GetProperties();
for (int i = 0; i < pis.Length; i++)
{
//if (pis[i].Name != PrimaryKey)
{
object objValue = pis[i].GetValue(obj, null);
objValue = (objValue == null) ? DBNull.Value : objValue;
if (!ht.ContainsKey(pis[i].Name))
{
ht.Add(pis[i].Name, objValue);
}
}
}
return ht;
}
#endregion
IBaseDAL接口#region IBaseDAL接口
/**//// <summary>
/// 查询数据库,检查是否存在指定键值的对象
/// </summary>
/// <param name="recordTable">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns>
public bool IsExistKey(Hashtable recordTable)
{
SqlParameter[] param = new SqlParameter[recordTable.Count];
IEnumerator eKeys = recordTable.Keys.GetEnumerator();
string fields = "";// 字段名
int i = 0;
while (eKeys.MoveNext())
{
string field = eKeys.Current.ToString();
fields += string.Format(" {0} = @{1} AND", field, field);
string val = recordTable[eKeys.Current.ToString()].ToString();
param[i] = new SqlParameter(string.Format("@{0}",field), val);
i++;
}
fields = fields.Substring(0, fields.Length - 3);//除去最后的AND
string sql = string.Format("SELECT COUNT(*) FROM {0} WHERE {1}", tableName, fields);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
command.Parameters.AddRange(param);
return (int)db.ExecuteScalar(command) > 0;
}
/**//// <summary>
/// 查询数据库,检查是否存在指定键值的对象
/// </summary>
/// <param name="fieldName">指定的属性名</param>
/// <param name="key">指定的值</param>
/// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns>
public bool IsExistKey(string fieldName, object key)
{
Hashtable table = new Hashtable();
table.Add(fieldName, key);
return IsExistKey(table);
}
/**//// <summary>
/// 获取数据库中该对象的最大ID值
/// </summary>
/// <returns>最大ID值</returns>
public int GetMaxID()
{
string sql = string.Format("SELECT MAX({0}) AS MaxID FROM dbo.{1}", primaryKey, tableName);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
object obj = db.ExecuteScalar(command);
if(Convert.IsDBNull(obj))
{
return 0;//没有记录的时候为0
}
return Convert.ToInt32(obj);
}
/**//// <summary>
/// 根据指定对象的ID,从数据库中删除指定对象(用于整型主键)
/// </summary>
/// <param name="key">指定对象的ID</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public bool DeleteByKey(string key)
{
string condition = string.Format("{0} = '{1}'", primaryKey, key);
return DeleteByCondition(condition);
}
/**//// <summary>
/// 根据指定条件,从数据库中删除指定对象
/// </summary>
/// <param name="condition">删除记录的条件语句</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public bool DeleteByCondition(string condition)
{
string sql = string.Format("DELETE FROM dbo.{0} WHERE {1} ", tableName, condition);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
return db.ExecuteNonQuery(command) > 0;
}
#endregion
}
3、具体的数据访问类
基类完成所有的操作了,对于具体的类将是一大福音,说明它的工作减少很多了,下面看看具体的实现过程。定义一个数据访问类接口,然后实现接口和继承基类即可。
public interface IEquipment : IBaseDAL<EquipmentInfo>
{
}
public class Equipment : BaseDAL<EquipmentInfo>, IEquipment
{
对象实例及构造函数#region 对象实例及构造函数
public static Equipment Instance
{
get
{
return new Equipment();
}
}
public Equipment() : base("Equipment","ID")
{
}
#endregion
}
其实这样就完成了,我们为了提高效率,重载两个函数的实现,避免基类的属性反射带来的性能损失,这两个函数看似很复杂,其实通过代码生成工具,生成起来也是毫不费功夫的。。
protected override EquipmentInfo DataReaderToEntity(IDataReader dataReader)
protected override Hashtable GetHashByEntity(EquipmentInfo obj)
因此最后的代码就变为下面
public class Equipment : BaseDAL<EquipmentInfo>, IEquipment
{
对象实例及构造函数#region 对象实例及构造函数
public static Equipment Instance
{
get
{
return new Equipment();
}
}
public Equipment() : base("Equipment","ID")
{
}
#endregion
/**//// <summary>
/// 将DataReader的属性值转化为实体类的属性值,返回实体类
/// </summary>
/// <param name="dr">有效的DataReader对象</param>
/// <returns>实体类对象</returns>
protected override EquipmentInfo DataReaderToEntity(IDataReader dataReader)
{
EquipmentInfo equipmentInfo = new EquipmentInfo();
SmartDataReader reader = new SmartDataReader(dataReader);
equipmentInfo.ID = reader.GetInt32("ID");
equipmentInfo.PartID = reader.GetString("PartID");
equipmentInfo.Name = reader.GetString("Name");
equipmentInfo.EquipmentType = reader.GetString("EquipmentType");
equipmentInfo.Specification = reader.GetString("Specification");
equipmentInfo.Manufacturer = reader.GetString("Manufacturer");
equipmentInfo.Picture = reader.GetBytes("Picture");
equipmentInfo.ApplyEquipment = reader.GetString("ApplyEquipment");
equipmentInfo.BuyAmount = reader.GetInt32("BuyAmount");
equipmentInfo.BuyDate = reader.GetDateTime("BuyDate");
equipmentInfo.Status = reader.GetString("Status");
equipmentInfo.UserName = reader.GetString("UserName");
equipmentInfo.SafeNumber = reader.GetInt32("SafeNumber");
equipmentInfo.Note = reader.GetString("Note");
return equipmentInfo;
}
/**//// <summary>
/// 将实体对象的属性值转化为Hashtable对应的键值
/// </summary>
/// <param name="obj">有效的实体对象</param>
/// <returns>包含键值映射的Hashtable</returns>
protected override Hashtable GetHashByEntity(EquipmentInfo obj)
{
EquipmentInfo info = obj as EquipmentInfo;
Hashtable hash = new Hashtable();
hash.Add("ID", info.ID);
hash.Add("PartID", info.PartID);
hash.Add("Name", info.Name);
hash.Add("EquipmentType", info.EquipmentType);
hash.Add("Specification", info.Specification);
hash.Add("Manufacturer", info.Manufacturer);
hash.Add("Picture", info.Picture);
hash.Add("ApplyEquipment", info.ApplyEquipment);
hash.Add("BuyAmount", info.BuyAmount);
hash.Add("BuyDate", info.BuyDate);
hash.Add("Status", info.Status);
hash.Add("UserName", info.UserName);
hash.Add("SafeNumber", info.SafeNumber);
hash.Add("Note", info.Note);
return hash;
}
}
文章太长,下面关于逻辑层、缓存、界面部分的设计在下一篇文章中介绍。
以上所引用的代码是通过代码生成工具Database2Sharp自动生成(http://www.iqidi.com/Database2Sharp.htm),选择EnterpriseLibrary架构即可。
接着上一篇关于分层架构的讨论,一个分层架构设计的例子(1)。
上篇介绍了实体类(Entity)、数据库访问类(DAL)、数据访问接口(IDAL)的相关设计,本篇主要讨论下面几个部分内容:业务逻辑层、缓存机制、界面层等方面。
业务逻辑层,主要是业务逻辑基类的设计,由于数据库访问类(DAL)的基类封装了大量的操作实现,因此,业务逻辑层的主要工作是进一步封装对底层访问接口的实现,如下所示。
public class BaseBLL<T> where T : BaseEntity, new()
{
构造函数#region 构造函数
private string dalName = "";
protected IBaseDAL<T> baseDal = null;
public BaseBLL()
: this("")
{
}
public BaseBLL(string dalName)
{
this.dalName = dalName;
if (string.IsNullOrEmpty(dalName))
{
this.dalName = GetType().Name;
}
baseDal = Reflect<IBaseDAL<T>>.Create(this.dalName, "HuaweiSoftware.IPSPBD.DAL");
}
#endregion
对象添加、修改、删除等接口#region 对象添加、修改、删除等接口
/**//// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <returns>执行成功返回新增记录的自增长ID。</returns>
public virtual bool Insert(T obj)
{
return baseDal.Insert(obj);
}
/**//// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool Update(T obj, string primaryKeyValue)
{
return baseDal.Update(obj, primaryKeyValue);
}
/**//// <summary>
/// 查询数据库,检查是否存在指定ID的对象(用于字符型主键)
/// </summary>
/// <param name="key">对象的ID值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
public virtual T FindByID(string key)
{
return baseDal.FindByID(key);
}
/**//// <summary>
/// 查询数据库,检查是否存在指定键值的对象
/// </summary>
/// <param name="fieldName">指定的属性名</param>
/// <param name="key">指定的值</param>
/// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool IsExistKey(string fieldName, object key)
{
return baseDal.IsExistKey(fieldName, key);
}
/**//// <summary>
/// 根据指定对象的ID,从数据库中删除指定对象(用于整型主键)
/// </summary>
/// <param name="key">指定对象的ID</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool Delete(string key)
{
return baseDal.DeleteByKey(key);
}
/**//// <summary>
/// 根据指定条件,从数据库中删除指定对象
/// </summary>
/// <param name="condition">删除记录的条件语句</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool DeleteByCondition(string condition)
{
return baseDal.DeleteByCondition(condition);
}
#endregion
返回集合的接口#region 返回集合的接口
/**//// <summary>
/// 根据ID字符串(逗号分隔)获取对象列表
/// </summary>
/// <param name="idString">ID字符串(逗号分隔)</param>
/// <returns>符合条件的对象列表</returns>
public virtual List<T> FindByIDs(string idString)
{
return baseDal.FindByIDs(idString);
}
/**//// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="condition">查询的条件</param>
/// <returns>指定对象的集合</returns>
public virtual List<T> Find(string condition)
{
return Find(condition);
}
/**//// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="condition">查询的条件</param>
/// <param name="info">分页实体</param>
/// <returns>指定对象的集合</returns>
public virtual List<T> Find(string condition, PagerInfo info)
{
return baseDal.Find(condition, info);
}
/**//// <summary>
/// 返回数据库所有的对象集合
/// </summary>
/// <returns>指定对象的集合</returns>
public virtual List<T> GetAll()
{
return baseDal.GetAll();
}
/**//// <summary>
/// 返回数据库所有的对象集合(用于分页数据显示)
/// </summary>
/// <param name="info">分页实体信息</param>
/// <returns>指定对象的集合</returns>
public virtual List<T> GetAll(PagerInfo info)
{
return baseDal.GetAll(info);
}
public virtual DataSet GetAllToDataSet(PagerInfo info)
{
return baseDal.GetAllToDataSet(info);
}
#endregion
}
业务层基类封装了大量的调用,那么对于业务层的具体操作类,它的工作就很简单了,基本上只需要继承一下基类就可以了,这就是有一个优秀父亲的好处,呵呵
public class Equipment : BaseBLL<EquipmentInfo>
{
public Equipment() : base()
{
}
}
基本上,业务层的设计到此应该收尾了,可是我们注意到,很多开发都使用了缓存的机制来进一步提高程序的性能,下面对这方面进行讨论。缓存的机制,一般是把创建过的对象资源放到一个集合中,需要的时候,调出来,如下业务层的工厂类所示。
public class BLLFactory<T> where T : class
{
private static Hashtable objCache = new Hashtable();
public static T Instance
{
get
{
string CacheKey = typeof(T).FullName;
T bll = (T)objCache[CacheKey]; //从缓存读取
if (bll == null)
{
bll = Reflect<T>.Create(typeof(T).Name, "HuaweiSoftware.IPSPBD.BLL"); //反射创建,并缓存
}
return bll;
}
}
}
这是一个业务逻辑类工厂创建类,我们在界面层只需要如下调用即可构造一个(利用了缓存)具体的业务类出来
CustomerInfo info = BLLFactory<Customer>.Instance.FindByID(ID);
在上面的BaseBLL和BLLFactory类中,有一个Reflect的操作类,这是反射缓存的具体实现所在,我们探讨一下它的实现。
public class Reflect<T> where T : class
{
private static Hashtable m_objCache = null;
public static Hashtable ObjCache
{
get
{
if (m_objCache == null)
{
m_objCache = new Hashtable();
}
return m_objCache;
}
}
public static T Create(string sName, string sFilePath)
{
return Create(sName, sFilePath, true);
}
public static T Create(string sName, string sFilePath, bool bCache)
{
string CacheKey = sFilePath + "." + sName;
T objType = null;
if (bCache)
{
objType = (T)ObjCache[CacheKey]; //从缓存读取
if (!ObjCache.ContainsKey(CacheKey))
{
Assembly assObj = CreateAssembly(sFilePath);
object obj = assObj.CreateInstance(CacheKey);
objType = (T)obj;
ObjCache.Add(CacheKey, objType);// 写入缓存 将DAL内某个对象装入缓存
}
}
else
{
objType = (T)CreateAssembly(sFilePath).CreateInstance(CacheKey); //反射创建
}
return objType;
}
public static Assembly CreateAssembly(string sFilePath)
{
Assembly assObj = (Assembly)ObjCache[sFilePath];
if (assObj == null)
{
assObj = Assembly.Load(sFilePath);
ObjCache.Add(sFilePath, assObj);//将整个DLL装入缓存
}
return assObj;
}
}
另外,如果你在业务层需要实现更加复杂的功能,而数据库访问基类BaseDAL提供的函数不能满足你的需要,可以扩展数据访问层的接口和实现,如下所示。
public interface ICustomer : IBaseDAL<CustomerInfo>
{
List<string> GetAllCustomerNumber();
CustomerInfo GetByCustomerNumber(string number);
}
public class Customer : BaseDAL<CustomerInfo>, ICustomer
{
对象实例及构造函数#region 对象实例及构造函数
public static Customer Instance
{
get
{
return new Customer();
}
}
public Customer() : base("All_Customer","ID")
{
}
#endregion
ICustomer 成员#region ICustomer 成员
public List<string> GetAllCustomerNumber()
{
string sql = string.Format("Select Number From dbo.{0}", tableName);
List<string> list = new List<string>();
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
string number = string.Empty;
using (IDataReader dr = db.ExecuteReader(command))
{
while (dr.Read())
{
number = dr["Number"].ToString();
if (!string.IsNullOrEmpty(number))
{
list.Add(number);
}
}
}
return list;
}
public CustomerInfo GetByCustomerNumber(string number)
{
string condition = string.Format("Number = '{0}'", number);
List<CustomerInfo> list = base.Find(condition);
if (list.Count > 0)
{
return list[0];
}
else
{
return null;
}
}
#endregion
}
那么在业务层的类修改如下
public class Customer : BaseBLL<CustomerInfo>
{
public Customer() : base()
{
}
public List<string> GetAllCustomerNumber()
{
ICustomer customerDAL = baseDal as ICustomer;
return customerDAL.GetAllCustomerNumber();
}
public CustomerInfo GetByCustomerNumber(string number)
{
ICustomer customerDAL = baseDal as ICustomer;
return customerDAL.GetByCustomerNumber(number);
}
}
最后,界面方面的设计是见仁见智,但根本一条是利用一些控件,可以统一风格,减少劳动,给出几个界面的设计截图供大家参考
WinForm方面的(颜色标明的是使用了特定的界面控件,其中红色部分为和整个架构整合起来的分页控件,集成了一些基本的右键菜单操作,包括打印功能、数据导出功能等):
Winform分页控件设计视图
可以选择列进行打印
在实际运用过程中的界面效果
WebForm方面的(可以使用之前文章介绍的查询控件、分页控件、内容编辑控件):
下图是查询控件和分页控件的一起运用:
修改内容时候的编辑控件
查看内容时候的编辑控件
以上所引用的代码是通过代码生成工具Database2Sharp自动生成(http://www.iqidi.com/Database2Sharp.htm),选择EnterpriseLibrary架构即可。
转载请注明出处: 撰写人:伍华聪 http://www.iqidi.com
|