最近许多人似乎都在研究orm,也看到了各种各样的代码生成器,不得不让人思索,究竟orm能做到什么极至呢?代码生成到底是工具还是一种设计思想呢?
·谈到orm,很多开始的话题就是table到class还是class到table,我觉得什么到什么都不太所谓,因为从uml工具的类图自动导出class文件和table似乎同样容易,那也就没什么争论的必要了,对于较简单的业务逻辑,如果对象间关系还不是很复杂的话,直接先建表,再生成class可能更好,起码比xpo这样在class里写一大堆惹人眼的自定义的attribute看着舒服,代码要干净得多吧?
·再说是不是有必要生成dal的问题,有朋友说因为可能后台用oo数据库,所以意义不大,这种说法就比较奇怪了,因为既然是orm,object relation map,自然是针对关系型数据库的啦。
·dal生成代码又要生成到什么程度呢?争议就在要不要生成sql语句了,我的意见是肯定要生成啦,虽然各数据库厂商的sql语句语法不尽相同,但基本语句总还是一致的吧,代码生成的原则就是最大程度减少重复写的代码,换句话说,就是凡是平时我们发现写过两遍以上的重复代码都是有可能被自动化的,那为何不自动化呢?oo的思想教导我们,把共性抽象出来,把特殊留给继承类来做,生成sql语句,同样可以这样做啊!
·既然要生成sql,那么总要生成一个相对完备的集合,我不知道各位都生成哪些,我的意见是除了基本的insert,delete,update,要多做点工作的话就是在select上了,有select单行数据,多行数据,指定某一页的数据,指定条件记录count这些应该差不多了,以上这些操作的组合应该可以完成大多数的正常数据操作了。
·再说要不要生成到业务逻辑的层次的问题,在这一点很容易让人犹豫,因为,这里面涉及两方面的主要难点:一是,不同于关系型数据库的数据访问层的操作相似和可预知性,到业务逻辑往往就不可预知了,特别对于复杂的业务逻辑,根本无从谈规律,也就谈不上代码生成;二、即使是非常简单的业务逻辑,往往要做一些重要的事务处理,那么,要自动生成代码就不得不有一种方式来描述这些事务估集合关系,怎么描述呢?想来想去是不容易的事,就算可以自己定个xml来描述,维护这样一个xml配置文件的代价可能已经不必自己手写整个业务逻辑层来得小了。
·上面一段还是没有下个结论,既然复杂不成,简单的也有障碍,那么是不是干脆不生成呢?我觉得也不该一棒打死,关键还是看是不是在业务逻辑层面上有不少的重复代码要写,为了更清晰的区分业务逻辑层我们一般要做的事,我试图把业务逻辑层的主要任务分成两块,一块是真正和对于于复杂应用来讲需要专业领域逻辑,这一部分,应该说肯定是不适合自动生成的,另一块,不如称作事务逻辑,就是对某个类的删除、添加、更新这样的操作,往往要做的不是一个单一操作,要和一些附加操作放进一个事务中,这一块,我觉得起码还是可能生成的。但是正如上一节所说,生成这样的事务逻辑,需要有一个配置文件,至少在目前,维护这样一个配置文件的代价一般来讲都不低,所以配置文件的做法并不十分可取,就没有什么取巧的办法吗?在这一点上我犹豫了很久,最后,在我的一个试验代码生成的小工具中是这样做的,大家看看和不合理:就是,不生成这些事务保护代码,但是,预留下用户添加代码的事务代码框架,默认是注释状态的,取消注释,就能添加方便的添加事务保护支持,例如下面这样:
namespace CN.Teddy.Helper.WebTest.BusinessLogic


{
public sealed class TableBLogic

{
private TableBLogic()

{
}
public static void CreateTableBInfo(TableBInfo obj)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//TableBAccess.InsertTableBInfo(obj, tran);
TableBAccess.InsertTableBInfo(obj);
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static void UpdateTableBInfo(TableBInfo obj)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//TableBAccess.UpdateTableBInfo(obj, new Condition("ID", "ID", OP.Equals, obj.ID), tran);
TableBAccess.UpdateTableBInfo(obj, new Condition("ID", "ID", OP.Equals, obj.ID));
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static void UpdateTableBInfo(string[] updateColumns, object[] updateValues, Condition condition)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//TableBAccess.UpdateTableBInfo(updateColumns, updateValues, condition, tran);
TableBAccess.UpdateTableBInfo(updateColumns, updateValues, condition);
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static void UpdateTableBInfo(TableBInfo obj, Condition condition)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//TableBAccess.UpdateTableBInfo(obj, condition, tran);
TableBAccess.UpdateTableBInfo(obj, condition);
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static void DeleteTableBInfo(Condition condition)

{
int[] bIDs = TableBAccess.SelectTableBInfoIDs(condition);
IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
if (bIDs != null && bIDs.Length > 0)

{
foreach (int id in bIDs)

{
TableAAccess.DeleteTableAInfo(new Condition("A7", "BID", OP.Equals, id), tran);
}
}
TableBAccess.DeleteTableBInfo(condition, tran);
//TableBAccess.DeleteTableBInfo(condition);
tran.Commit();
}
catch(Exception e)

{
tran.Rollback();
throw e;
}
finally

{
if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)

{
tran.Connection.Close();
}
}
}
public static void DeleteTableBInfo(int id)

{
IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
TableAAccess.DeleteTableAInfo(new Condition("A7", "BID", OP.Equals, id), tran);
TableBAccess.DeleteTableBInfo(new Condition("ID", "ID", OP.Equals, id), tran);
//TableBAccess.DeleteTableBInfo(new Condition("ID", "ID", OP.Equals, id));
tran.Commit();
}
catch(Exception e)

{
tran.Rollback();
throw e;
}
finally

{
if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)

{
tran.Connection.Close();
}
}
}
public static TableBInfo GetTableBInfo(int id)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//return TableBAccess.SelectTableBInfo(id, tran);
return TableBAccess.SelectTableBInfo(id);
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static IList GetTableBInfo(Condition condition, OrderList orderList)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//return TableBAccess.SelectTableBInfo(condition, orderList, tran);
return TableBAccess.SelectTableBInfo(condition, orderList);
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static int[] GetTableBInfoIDs(Condition condition)

{
//IDbTransaction tran = Configuration.BeginTransaction();
try

{
//If need transaction, uncomment these commented lines, and add other operations here
//return TableBAccess.SelectTableBInfoIDs(condition, tran);
return TableBAccess.SelectTableBInfoIDs(condition);
//tran.Commit();
}
catch(Exception e)

{
//tran.Rollback();
throw e;
}
//finally
//{
// if (tran.Connection != null && tran.Connection.State == ConnectionState.Open)
// {
// tran.Connection.Close();
// }
//}
}
public static PageAdapter GetTableBInfoPageAdapter(Condition condition, OrderList orderList)

{
try

{
return new PageAdapter(TableBAccess.SelectTableBInfoPageSplit(condition, orderList), new CreateObjectListHandler(TableBInfo.CreateObjectList));
}
catch(Exception e)

{
throw e;
}
}
}
}
·附录:用本人的代码生成器生成的值对象、数据访问代码示例
1、值对象(为每个值对象生成了一些辅助静态方法:对象构造、返回成员名称、返回成员值列表等)
// Code Generated By Code Generator For CN.Teddy.Helper.Data V2.0
// 2005-4-3 9:32:14
using System;
namespace CN.Teddy.Helper.WebTest.ValueObject


{
[Serializable]
public class TableBInfo

{
private int _ID;
private string _B1;
private byte _B2;
public int ID

{

get
{ return _ID; }

set
{ _ID = value; }
}
public string B1

{

get
{ return _B1; }

set
{ _B1 = value; }
}
public byte B2

{

get
{ return _B2; }

set
{ _B2 = value; }
}

Static Members#region Static Members

private static string[] _Members = new string[]
{ "ID", "B1", "B2" };
public static string[] GetMemberNames(params string[] exceptMembers)

{
if (exceptMembers == null)

{
return _Members;
}
else

{
string[] retMembers = new string[_Members.Length - exceptMembers.Length];
int i = 0;
foreach (string item in _Members)

{
bool isExcept = false;
foreach (string str in exceptMembers)

{
if (item == str)

{
isExcept = true;
break;
}
}
if (!isExcept)

{
retMembers[i++] = item;
}
}
return retMembers;
}
}
public static object[] GetMemberValues(TableBInfo obj, params string[] exceptMembers)

{

object[] values = new object[]
{ obj.ID, obj.B1, obj.B2 };
if (exceptMembers == null)

{
return values;
}
else

{
object[] retValues = new object[_Members.Length - exceptMembers.Length];
int i = 0;
int j = 0;
foreach (string item in _Members)

{
bool isExcept = false;
foreach (string str in exceptMembers)

{
if (item == str)

{
isExcept = true;
break;
}
}
if (!isExcept)

{
retValues[i++] = values[j];
}
j++;
}
return retValues;
}
}
public static TableBInfo CreateObject(System.Data.DataRow row)

{
if (row == null)

{
return null;
}
TableBInfo newObj = new TableBInfo();
object item = null;
item = row["ID"];
if (item != DBNull.Value)

{
newObj.ID = (int)item;
}
item = row["B1"];
if (item != DBNull.Value)

{
newObj.B1 = (string)item;
}
item = row["B2"];
if (item != DBNull.Value)

{
newObj.B2 = (byte)item;
}
return newObj;
}
public static System.Collections.IList CreateObjectList(System.Data.DataTable table)

{
if (table == null)

{
return null;
}
System.Collections.ArrayList retList = new System.Collections.ArrayList();
foreach (System.Data.DataRow row in table.Rows)

{
retList.Add(CreateObject(row));
}
return retList;
}
public static System.Collections.IList CreateObjectList(System.Data.IDataReader rdr)

{
if (rdr == null)

{
return null;
}
System.Collections.ArrayList retList = new System.Collections.ArrayList();
System.Data.DataTable table = new System.Data.DataTable();
for (int i = 0; i < rdr.FieldCount; i++)

{
table.Columns.Add(rdr.GetName(i), rdr.GetFieldType(i));
}
while (rdr.Read())

{
object[] rowObjs = new object[rdr.FieldCount];
rdr.GetValues(rowObjs);
table.Rows.Clear();
table.Rows.Add(rowObjs);
retList.Add(CreateObject(table.Rows[0]));
}
return retList;
}
#endregion
}
}
2、数据访问类(其中DataAccessHelper等是一些助手类,用于执行具体的操作,这些助手类的基本思想借鉴与entlib的da block)
// Code Generated By Code Generator For CN.Teddy.Helper.Data V2.0
// 2005-4-4 11:35:46
using System;
using System.Collections;
using System.Data;
using CN.Teddy.Helper.Data;
using CN.Teddy.Helper.DataAccess;
using CN.Teddy.Helper.WebTest.ValueObject;
namespace CN.Teddy.Helper.WebTest.DataAccess


{
public sealed class TableBAccess

{
private TableBAccess()

{
}
public static void InsertTableBInfo(TableBInfo obj, params IDbTransaction[] trans)

{
DbHelper.Insert(Configuration.DB, "TableBInfo", TableBInfo.GetMemberNames("ID"), TableBInfo.GetMemberValues(obj, "ID"), trans);
}
public static void UpdateTableBInfo(TableBInfo obj, Condition condition, params IDbTransaction[] trans)

{
DbHelper.Update(Configuration.DB, "TableBInfo", TableBInfo.GetMemberNames("ID"), TableBInfo.GetMemberValues(obj, "ID"), (condition == null ? null : condition.Where), (condition == null ? null : condition.WhereValues), trans);
}
public static void UpdateTableBInfo(string[] updateColumns, object[] updateValues, Condition condition, params IDbTransaction[] trans)

{
DbHelper.Update(Configuration.DB, "TableBInfo", updateColumns, updateValues, (condition == null ? null : condition.Where), (condition == null ? null : condition.WhereValues), trans);
}
public static void DeleteTableBInfo(Condition condition, params IDbTransaction[] trans)

{
DbHelper.Delete(Configuration.DB, "TableBInfo", (condition == null ? null : condition.Where), (condition == null ? null : condition.WhereValues), trans);
}
public static TableBInfo SelectTableBInfo(int id, params IDbTransaction[] trans)

{

DataSet ds = DbHelper.Select(Configuration.DB, "TableBInfo", TableBInfo.GetMemberNames(), "[ID] = @ID", new object[]
{id}, null, trans);
if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)

{
return TableBInfo.CreateObject(ds.Tables[0].Rows[0]);
}
return null;
}
public static IList SelectTableBInfo(Condition condition, OrderList orderList, params IDbTransaction[] trans)

{
DataSet ds = DbHelper.Select(Configuration.DB, "TableBInfo", TableBInfo.GetMemberNames(), (condition == null ? null : condition.Where), (condition == null ? null : condition.WhereValues), (orderList == null ? null : orderList.OrderBy), trans);
if (ds != null && ds.Tables.Count > 0)

{
return TableBInfo.CreateObjectList(ds.Tables[0]);
}
return null;
}
public static int[] SelectTableBInfoIDs(Condition condition, params IDbTransaction[] trans)

{

DataSet ds = DbHelper.Select(Configuration.DB, "TableBInfo", new string[]
{"ID"}, (condition == null ? null : condition.Where), (condition == null ? null : condition.WhereValues), null, trans);
if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)

{
int[] retIDs = new int[ds.Tables[0].Rows.Count];
int i = 0;
foreach (DataRow row in ds.Tables[0].Rows)

{
retIDs[i++] = (int)row["ID"];
}
return retIDs;
}
return null;
}
public static IPageSplit SelectTableBInfoPageSplit(Condition condition, OrderList orderList)

{
return DbHelper.Select(Configuration.DB, "TableBInfo", TableBInfo.GetMemberNames(), (condition == null ? null : condition.Where), (orderList == null ? null : orderList.OrderBy), "ID", (condition == null ? null : condition.WhereValues));
}
}
}
·小弟比较懒,写这个代码生成器的目的主要是从为自己减少手工编写代码量出发,所以有些地方实现未必优雅,欢迎批评指正!