首先声明一下,小虾的写文章的水平那是相当的两字——很菜!所以碰到看不明白的文字,请大家留言,我会作特别回答。
1、支持差异数据保存
如果想做到这一点, 大家最容易想到的就是添加一个标记,用于记录当前属性的值是否被修改过。
先上一段传统的数据库实体类的设计方法:
{
public int IntValue{get; set ;}
public int StringValue{get; set ;}
public int BoolValue{get; set ;}
public int DoubleValue{get; set ;}
public int DateTimeValue{get; set ;}
}
如果简单的通过在实体类中添加一个标记来记录每个属性是否被修改过,当然是不可行的,因为你根本就不知道每个实体有多少个属性,而且你还必须在每个标记和属性之间建立联系.这个时候,一个数据类型对象浮现在脑海中,就是放弃传统的int, bool, string, double, DateTime这些数据类型,当然也不是完全放弃,只是对这些简单类型进行一次封装,这样字一个初步的数据类型对象就产生了:
2 {
3 public int Value{get; set; }
4 public bool ValueChanged{get; set; }
5 }
这样传统的数据库实体类就要做适当的调整了:
2 {
3 public MInt IntValue{get;set;}
4 public MString StringValue{get;set;}
5 public MBool BoolValue{get;set;}
6 public MDouble DoubleValue{get;set;}
7 public MDateTime DateTimeValue{get;set;}
8 }
为了能够更好的支持DBNull类型的数据保存到数据库,需要在数据类型对象添加一个标记IsNull,这样在进行数据保存只要判断对象是否为空,如果为空,则在更新,或者插入数据的时候,直接赋值DBNull.Value即可,这里只是对数据类型对象其中的一小块扩展,下面还会有很多其他的,请继续往下走。
2、0查询语句
说到0查询语句,小虾在这里说了句大话了,不管怎样,先看看小虾是如何实现的吧。
小虾的查询语句包括4种类型:Select, Delete, Update, Insert;做过开发的人都知道,每次新增加一个实体对象,都需要写一大堆的查询语句,简直是揪心。不过等你看了这篇文章以后,相信你从此就不用再揪心了。
文章开头提到了0反射,但是不通过反射又如何获取实体对象中的每一个属性的名称和值呢?大家很容易想到的就是枚举了,对,我也是这么想的,通过集合的方式,将实体对象中的所有属性都装到集合中,小虾准备用IList<T>。但是新的问题又出现了我们的实体对象的属性的类型是MInt, MBool, MString。。。如何让他能够支持IList<T>呢。所有需要对原始的数据类型对象进行一下修改了,让他们全部继承自一个对象TypeBase不就行了吗?这样一个数据类型对象基类就产生了:
2 {
3 /// <summary>
4 /// 值
5 /// </summary>
6 public abstract object ObjectValue { get; set; }
7
8 /// <summary>
9 /// 是否为空
10 /// </summary>
11 public abstract bool IsNull { get; set; }
12
13 /// <summary>
14 /// 值是否改变
15 /// </summary>
16 public abstract bool ValueChanged { get; set; }
17 }
按照上文将的,为了能够达到遍历实体对象内部的每一个属性,而且达到0查询语句的效果,我们的自定义数据库实体类就应该再一次进行修改了:
{
#region 静态对象, 数据库表名,字段名
//表名
public static string TABLENAME = "NEWENTITY";
//数据库字段名
public static string INTVALUE= "IntValue";
public static string STRINGVALUE= "StringValue";
public static string BOOLVALUE= "BoolValue";
public static string DOUBLEVALUE= "DoubleValue";
public static string DATETIMEVALUE= "DateTimeValue";
#endregion
#region 属性成员
//属性结合,用于存放该实体对象的所有属性,以便于之后的遍历
public IList<TypeBase> ListField{get; private set;}
//数据表名,一边能够通过实例成员获取到该实体对象所对应的数据库表名
public string TableName {get{ return TABLENAME; }}
//实体属性
public MInt IntValue{get;set;}
public MString StringVa;}
public MBool BoolValue{get;set;}
public MDouble DoubleValue{get;set;}
public MDateTime DateTime;}
#endregion
#region 构造函数
//无参数构造
//在构造对象的时候,小虾将每个字段的数据库字段的字段名引用传输属性中保存,也是为了方便只有在遍历
//属性集合的时候,能够获取该属性的值,同时能够获取到该属性对于的数据库字段名
//用ref传参,是尽量保证一个数据库字段名,在内存中只有一个引用,在一定程度上减少了内存开销
public NewEntity()
{
this.IntValue = new IntValue(ref INTVALUE);
this.StringValue= new StringValue(ref STRINGVALUE);
this.BoolValue= new IntValue(ref BOOLVALUE);
this.DoubleValue= new DoubleValue(ref DOUBLEVALUE);
this.DateTimeValue= new DateTimeValue(ref DATETIMEVALUE);
///将所有属性添加到属性集合中
this._AddFieldToList();
}
//DataRow对象构造
public NewEntity(DataRow row)
{
this.IntValue = Convert.ToMyInt(row[INTVALUE], ref INTVALUE);
this.StringValue= Convert.ToMyString(row[STRINGVALUE], ref STRINGVALUE);
this.BoolValue= Convert.ToMyBool(row[BOOLVALUE], ref BOOLVALUE);
this.DoubleValue= Convert.ToMyDouble(row[DOUBLEVALUE], ref DOUBLEVALUE);
this.DateTimeValue= Convert.ToMyDateTime(row[DATETIMEVALUE], ref DATETIMEVALUE);
///将所有属性添加到属性集合中
this._AddFieldToList();
}
#endregion
#region 私有方法
///将所有属性添加到属性集合中
private void _AddFieldToList()
{
if(this.ListField == null) this.ListField = new List<TypeBase>();
this.ListField.Add(this.IntValue);
this.ListField.Add(this.StringValue);
this.ListField.Add(this.BoolValue);
this.ListField.Add(this.DoubleValue);
this.ListField.Add(this.DateTimeValue);
}
#endregion
}
展开代码,一看这么多行,可别直接一拖到底,仔细的研究一下,有了这样一个实体类才能够满足我们的0查询语句。
有一点要提出来的就是,我在每个实体属性对象中加了两个字段:ColumnName,该字段通过构造函数中给每个属性赋值的时候传入, 在这里还是贴一下代码,方便下文调用,同时也给大家少一些疑惑:
{
/// <summary>
/// 值
/// </summary>
public abstract object ObjectValue { get; set; }
/// <summary>
/// 是否为空
/// </summary>
public abstract bool IsNull { get; set; }
/// <summary>
/// 值是否改变
/// </summary>
public abstract bool ValueChanged { get; set; }
/// <summary>
/// 该属性对应数据库字段的字段名
/// </summary>
public abstract string ColumnName{ get; set; }
}
至于里面其他代码的用意,在这里就不多讲了,看注释基本上就能够懂了,还有问题的,请留言,我会做特别回复。
3、如何实现0查询语句
能看到这里的人,说明你真正关注的是,如何做到0查询语句,下面小虾就来讲讲如何实现0查询语句。
在将如何实现0查询语句之前,我这里有一个特别的说明,就是在每个数据库实体对象中,你必须保证有一个主键字段:MyInt ID,这样我们在update和delete的时候就可以通过这个唯一字段来处理。
直接上代码:
public bool Save()
{
if(this.Id.Value = 0) this.Insert();
else this.Update();
}
//插入对象
public bool Insert()
{
//这里通过一个公共方法,获取该对象的下一个Id
int Id = Utils.GetNextId(this.TableName);
string sql1 = "INSERT INTO " + tb.TableName + "(Id";
string sql2 = "(" + Id.ToString() + ",";
foreach(TypeBase tb in this.ListField)
{
sql1 += "," + tb.ColumnName;
//这里说明一下tb.ToString()方法需要在每一个种类型中进行重写
//如MyInt类型的对象的ToString()的返回值直接是ObjectValue.ToString();
//如MyString类型的对象的ToString()的返回值直接是"'" + ObjectValue.ToString() + "'";
//等等,其他的就不多说了。
sql2 += "," +tb.ToString();
}
sql1 +=") VALUES" + sql2 + ")";
//然后再执行数据库命令
return DbHelper.ExecCommand(sql);
}
//更新对象
public bool Update()
{
//更新对象的具体实现就不多啰嗦了,相信大家都会了,但是有一点需要提出的是,在update的时候,需要判断TypeBase.ValueChanged==true的时候,才做数据更行,否则不做处理,这样才能够达到文章题目的差异数据保存
}
//删除对象
public bool Delete()
{
//very easy.
//DELETE FROM TableName WHERE ID = ID....
}
看完代码,大家可能会有很多疑问,莫非每个对象都要写这么几个Save(), Insert(), Update(), Delete()方法,那岂不是比写sql语句还要累,或许你现在还会这么讲,但是相信凭你神一般的抽象能力,我想在一又三分子一秒之后,你肯定一拍脑袋,把数据库实体类抽象出来,然后把这几个方法放在基类中不久一切ok了吗,如果能想到这里,小虾只能说:哎,知音啊!
讲到这里,小虾的0查询语句就基本上是讲完了,或许你会说还有个查询语句没讲呢,小虾想说的是,查询功能就像打太极,几乎是千变万化, 无所不有,无坚不摧, 无...这些我就留到小虾接下来的文章中讲解。
接下来的文章可能包括:
1.自定义数据库实体类之基类抽象——ObjBase对象搭建
可能提供方法有:Save();Delete();DeleteById();GetListObject<T>(param1...);
2、自定义数据库实体类之查询语句对象——QueryString对象的实现
QueryString对象:类似Linq的数据库查询语句的写法,但是写法又更简单,更舒服,更。。。。请大家期待。
可能出现的语法有:QueryString.Select(...).From(...).Where(new Condition().And().Or()..).And().OrderBy();
ASP.NET开发技术交流群: 67511751(人员招募中...)