一步一步实现数据库到类的自动化映射(一) 类层次的设计 类的实现

上一篇讲了很多废话,结果才讲到了工具类。实在是有点浪费大家的时间。这次我就不多说了。马上来看看设计思路:我的目标是设计完成后。这个框架要可以使用工具来根据数据库表的结构自动生成类的代码,以便减少工作量。因此,我打算把类设计成只包含表的字段的所需信息。其它保存、和查询的操作都在基类里实现(好像是废话,人人都是这样设计类的-_-!)。

因此,我要有一个Attribute类来说明类的属性对数据表之间的映射(NHibernate是用XML来说明的,我不想搞得那么麻烦、方便就好)。

以一段设计好的代码来说明一下问题。

/// <summary>

         ///字段  T_VarChar

         ///数据库里的数据类型 :VarChar ,SqlDbType :VarChar

         ///说明:  字段长度:60 字段小数位数0

         ///默认值:   是否允许为空:True

         /// </summary>

         public event EventHandler T_VarCharChanged;

         private string mT_VarChar=null;

         [SqlFieldAttribute(SqlDbType = System.Data.SqlDbType.VarChar,FieldName = "T_Name")]

         public string T_VarChar

         {

              get{return mT_VarChar;}

              set

              {

                   mT_VarChar=value;

                   if (T_VarCharChanged!=null)

                       T_VarCharChanged(this,new EventArgs());

              }

         }

以上代码是一个类里的属性。他有一个自定义属性:

[SqlFieldAttribute(SqlDbType = System.Data.SqlDbType.VarChar,FieldName = "T_Namne")]

这个东西说明了以下信息:这个类的属性在数据库中将作为VarChar类型,它所映身到表的T_Name字段。

SqlFieldAttribute 类的定义如下:

public class SqlFieldAttribute : Attribute

     {

         public System.Data.SqlDbType SqlDbType = SqlDbType.VarChar;

         public string FieldName = "";

         public bool Saveable = true;

     }

子类还要重写(override) 基类的TableName方法,来获取这个类是映射哪一个表的。这个方法返回表名,如:

public override string TableName()

         {

              return "T_Test1";//这个类是对表T_Test1进行操作的。

         }

 

这样表和字段的映射关系都反映出来了,那么任何一个类都可以通过反射生成Sql语句来进行操作了。并且子类中就只包括了数据表的字段。这样将业要自动生成代码也是比较容易的。

 

重点来了:

本来Insert和Update操作可以放在基类里实现在的。可是为了要实现“事务操作”(同时确保几个类的保存成功与失败)。还是要另外编写一个工具类来保存这些信息。

这里先不看工具类,先看看基类是怎么实现的。

using System;

using System.Data;

using YZPTool;

namespace YZPTool.Data

{

     /// <summary>

     /// SqlDbRecordAbstract 的摘要说明。

     /// </summary>

     public abstract class SqlDbRecordAbstract

     {

         private int m_id1 = 0;

         private int m_id2 = 0;

         public int ID

         {

              get{return m_id1;}

         }

 

         public abstract string TableName();

 

         /// <summary>

         /// 从一条记录里读取信息给自己

         /// </summary>

         /// <param name="aDataRow">包含所有需要的信息的记录</param>

         public void LoadFromDataRow(DataRow aDataRow)

         {

              YZPTool.Data.DbClassTool.LoadFromDataRow(aDataRow,this);

         }

 

         /// <summary>

         /// 保存到数据库

         /// </summary>

         public void Save()

         {

              YZPTool.Data.DbClassTool.Save(this);//调用工具类的方法来保存自已

         }

 

/// <summary>

         /// 对表进行查询。

         /// </summary>

 

         public DataTable GetDataTable(string conditionString)

         {

              if ((conditionString!=null)||(conditionString!=""))

                   return DBManager.GetDataTable("select * from "+this.TableName()+" where "+conditionString);

              else

                   return DBManager.GetDataTable("select * from "+this.TableName());

         }

     }

}

 

要注意的地方是有两个私有的m_id成员.m_id1、m_id2;

m_id1是这个记录在数据库里的ID标识。当它>0时表示这个类已经是保存了在数据库里的了。回些调用Save时就是执行Update操作。如果是<0就调用Insert操作。

那为什么还有个m_id2呢。因为在事务操作时。要确保所有要保存的对像都成功保存到数据库了才能真正地更新m_id1这个值。在事务没有Comit前。都只是先把插入后的值放在了m_id2里暂时保存。

 

以下是通过反射来保存工具类的实现:

using System;

using System.Collections;

using System.Data;

using System.Data.SqlClient;

using System.Reflection;

using YZPTool;

 

namespace YZPTool.Data

{

     public class SqlFieldAttribute : Attribute

     {

         public System.Data.SqlDbType SqlDbType = SqlDbType.VarChar;

         public string FieldName = "";

         public bool Saveable = true;//这字段是否要更新到数据库。

     }

 

     /// <summary>

     /// DbClassTool 的摘要说明。

     /// </summary>

     public class DbClassTool

     {

         /// <summary>

         /// 这个是类包含了保存到数据库时的所有所需的信息如:字段名、字段类型,字段的值。

         /// </summary>

         protected class SqlField

         {

              public object FieldValue = null;

              public string FieldName = null;

              public SqlDbType SqlDbType = SqlDbType.VarChar;

              public bool Saveable = true;

              public SqlField(string aFieldName,object aFieldValue,SqlDbType aSqlDbType,bool saveable)

              {

                   FieldName = aFieldName;

                   FieldValue = aFieldValue;

                   SqlDbType = aSqlDbType;

                   Saveable = saveable;

              }

             

         }

 

         private DbClassTool()

         {}

 

         /// <summary>

         /// 获取一个类中包含的记录信息(SqlFiell

         /// </summary>

         /// <param name="obj">要对其操作的类</param>

         /// <returns>所有字段的信息:类型,值,字段名</returns>

         protected static IList GetSqlFields(SqlDbRecordAbstract obj)

         {

              ArrayList arrList = new ArrayList();

              Type type = obj.GetType();

              PropertyInfo[] propertyInfos = type.GetProperties(System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Public);

              foreach (PropertyInfo fi in propertyInfos)

              {

                   if (fi.IsDefined(typeof(SqlFieldAttribute),true))

                   {

                       SqlFieldAttribute sfa = (SqlFieldAttribute)fi.GetCustomAttributes(typeof(SqlFieldAttribute),true)[0];

                       SqlField sf = new SqlField(sfa.FieldName,fi.GetValue(obj,null),sfa.SqlDbType,sfa.Saveable);

                       arrList.Add(sf);

                   }

              }

              return arrList;

         }

 

         protected static void Insert(SqlCommand cmd,SqlDbRecordAbstract obj)

         {

              IList aSqlFields = DbClassTool.GetSqlFields(obj);

              cmd.Parameters.Clear();

              System.Text.StringBuilder sb = new System.Text.StringBuilder();

              sb.Append("Insert into "+obj.TableName()+" (");

              bool addComma = false;

              foreach (SqlField sf in aSqlFields)

              {

                   if (!sf.Saveable)//如果不需要保存,就不管他

                       continue;

                   if (addComma)

                       sb.Append(",");//第一次个字段不加逗号

                   else

                       addComma = true;

                   sb.Append(sf.FieldName);

              }

              sb.Append(") values(");

              addComma = false;

              foreach (SqlField sf in aSqlFields)

              {

                   if (!sf.Saveable)//如果不需要保存,就不管他

                       continue;

                   if (addComma)

                       sb.Append(",");//第一次个字段不加逗号

                   else

                       addComma = true;

                   sb.Append("@"+sf.FieldName);

                   //设置参数列表

                   System.Data.SqlClient.SqlParameter param = new System.Data.SqlClient.SqlParameter("@"+sf.FieldName,sf.SqlDbType);

                   if (sf.FieldValue!=null)

                       param.Value = sf.FieldValue;

                   else

                       param.Value = DBNull.Value;

                   cmd.Parameters.Add(param);

              }

              //sb.Append(");select @@IDENTITY as ID");

              sb.Append(");select Max(id) as ID from "+obj.TableName());

              FieldInfo fi_m_id2 = typeof(SqlDbRecordAbstract).GetField("m_id2",BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);

              cmd.CommandText = sb.ToString();

              object idValue = int.Parse(cmd.ExecuteScalar().ToString());

              //obj.m_id2 = int.Parse(idValue);

              fi_m_id2.SetValue(obj,idValue);

         }

 

         protected static void Update(SqlCommand cmd,SqlDbRecordAbstract obj)

         {

              IList aSqlFields = DbClassTool.GetSqlFields(obj);

              System.Text.StringBuilder sb = new System.Text.StringBuilder();

              sb.Append("Update "+obj.TableName()+" set ");

              bool addComma = false;

              foreach (SqlField sf in aSqlFields)

              {

                   if (!sf.Saveable)//如果不需要保存,就不管他

                       continue;

                   if (addComma)

                       sb.Append(",");//第一次个字段不加逗号

                   else

                       addComma = true;

                   sb.Append(sf.FieldName+"=@"+sf.FieldName);

                   //设置参数列表

                   System.Data.SqlClient.SqlParameter param = new System.Data.SqlClient.SqlParameter("@"+sf.FieldName,sf.SqlDbType);

                   if (sf.FieldValue!=null)

                       param.Value = sf.FieldValue;

                   else

                       param.Value = DBNull.Value;

                   cmd.Parameters.Add(param);

              }

              sb.Append(" where ID="+obj.ID.ToString());

              cmd.CommandText = sb.ToString();

              cmd.ExecuteNonQuery();

         }

 

         /// <summary>

         /// 外面就是调用这个方法来实现对象保存的事务机制的。

         /// </summary>

         /// <param name="aDbRecords"></param>

         public static void Save(SqlDbRecordAbstract[] aDbRecords)

         {

              SqlConnection conn = DBManager.GetConnection();

              conn.Open();

              SqlTransaction transaction = conn.BeginTransaction();

              SqlCommand sqlCommand = new SqlCommand("",conn,transaction);

              try

              {

                   //把类保存到数据库

                   foreach (SqlDbRecordAbstract obj in aDbRecords)

                   {

                       if (obj.ID<=0)

                            DbClassTool.Insert(sqlCommand,obj);

                       else

                            DbClassTool.Update(sqlCommand,obj);

                   }

                   //更新类的操作

                   foreach (SqlDbRecordAbstract obj in aDbRecords)

                   {

                       FieldInfo fi_m_id2 = typeof(SqlDbRecordAbstract).GetField("m_id2",BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);

                       FieldInfo fi_m_id1 = typeof(SqlDbRecordAbstract).GetField("m_id1",BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);

                       object idValue = fi_m_id2.GetValue(obj);

                       fi_m_id1.SetValue(obj,idValue);

                       //obj.m_id1 = obj.m_id2;

                   }

                   transaction.Commit();

              }

              catch (Exception ee)

              {

                   transaction.Rollback();

                   throw ee;

              }

              conn.Close();

         }

 

         /// <summary>

         /// 保存单个对象时就使用这个方法

         /// </summary>

         /// <param name="aDbRecord"></param>

         public static void Save(SqlDbRecordAbstract aDbRecord)

         {

              Save(new SqlDbRecordAbstract[]{aDbRecord});

         }

 

         /// <summary>

         /// 通过反射,从数把记录里读取信息。

         /// </summary>

         /// <param name="dataRow"></param>

         /// <param name="obj"></param>

         public static void LoadFromDataRow(DataRow dataRow,SqlDbRecordAbstract obj)

         {

              Type type = obj.GetType();

              PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);

              FieldInfo fi_m_id2 = typeof(SqlDbRecordAbstract).GetField("m_id2",BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);

              FieldInfo fi_m_id1 = typeof(SqlDbRecordAbstract).GetField("m_id1",BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);

              fi_m_id2.SetValue(obj,dataRow["id"]);

              fi_m_id1.SetValue(obj,dataRow["id"]);

              foreach(PropertyInfo pi in propertyInfos)

              {

                   if (pi.IsDefined(typeof(SqlFieldAttribute),true))

                   {

                       SqlFieldAttribute sfa = (SqlFieldAttribute)pi.GetCustomAttributes(typeof(SqlFieldAttribute),true)[0];

                       if (pi.CanWrite)

                            pi.SetValue(obj,dataRow[sfa.FieldName],null);

                       else

                        {

                            FieldInfo fi = type.GetField("m_"+pi.Name);

                            if (fi!=null)

                                 fi.SetValue(obj,dataRow[sfa.FieldName]);

                            else

                            {

                                 fi = type.GetField("m"+pi.Name);

                                 if (fi!=null)

                                     fi.SetValue(obj,dataRow[sfa.FieldName]);

                            }

                       }

                   }

              }

         }

     }

}

 

总结:只需使用三个类就能实现数据库表到对象的映射。

YZPTool.Data.SqlDbRecordAbstract、YZPTool.Data.DbClassTool、YZPTool.Data.SqlFieldAttribute

使用方法:子类只要继YZPTool.Data.SqlDbRecordAbstract,使用YZPTool.Data.SqlFieldAttribute对自已的属性与数据库的映射关系进行描述。在需要保存时调用自已的Save方法就可以更新、保存到数据库了。如果要同时保存多个对象时就调用YZPTool.Data.DbClassTool的表态方法public static void Save(SqlDbRecordAbstract[] aDbRecords)。

 

这个设计这几个类的时间不多。可能还有很多地方不能满足需求。大家有什么意见、或有更深的体会。希望可以交流,我的目标是做出一个简单易用的数据库框架出来。减少日常开发的工作量:

Yzp0755@163.com

 

类的层次暂时到此结束,欢迎大家讨论。如果我讲得不好,大家不要说脏话啊^_^。

 

下一篇我将会说说如何实现自动化生成子类的代码(本来想赶紧完成的,不过现在都快过年了,没时间啊。呵呵!!)。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/koy0755/archive/2006/01/21/585384.aspx

posted on 2009-09-28 23:08  Code007  阅读(312)  评论(0编辑  收藏  举报