认识DataQuicker(ORM)之一FieldMapping字段映射

(过时,请在我的Blog上参见最新文档)


在字段设计上,它与其他的O/R Mapping工具有点区别,在NHiberate或者Gentle.NET的字段映射通常直接以C#类型定义Property,比如,数据列UserName Char(31)

[自定义属性]

public string UserName
{
       get
       {
              return this.mUserName;
       }
       set
       {
              this.mUserName = value;
       }
}

我在设计DataQuicker时,我认为这样的设计不能更加直观的反应出数据列信息,对于数据列,不仅存储了内容/类型,还有长度、是否允许为空、默认值、有效性校验等。所以,DataQuicker对数据列进行了映射,现阶段的设计为:

FString   -    char, varchar, nchar, nvarchar等文本类型

FDateTime    -    日期类型

FBool     -    布尔类型,在MSSQL中对应bit类型

FInt -    整数类型

FDecimal      -    货币、浮点数在数据库中我通常设计为的decimal类型

FEnum   -    枚举,该类型更加偏向于应用,在应用系统中,我们通常有一些状态字段,比如Status:Good/Normal/Bad等

grafana 字段映射 字段映射英文_grafana 字段映射

 


 

在数据库中设置有MaxLength,对于通常当我们Update或者Insert时,需要手动去比较是否超长,或者我们还需要去判断哪些字段是否允许空值等等,这类工作会增加我们的负担,并且增大我们的维护/控管成本,比如在后期我们更改了数据列的长度设定,我们就需要去调整程序……而在DataQuicker初次加载时,会把相应的Database的Schema加载到内存中,因为字段映射类的设计,使DataQuicker的字段属性能够完成更多的任务,并且这些操作都是自动的,无须手动管理。在运行时阶段,可以自动及时的检测出数据的有效性和完整性(不与数据库通信的情况下),并且给予详细的错误清单,大大减少。


 

下面给一个示例:

在一张用户表User中,有一字段EmailAddress用户保存用户的电子邮件地址,定义为

EmailAddress varchar(127) not null,没有默认值

根据定义,我们可以得到该字段最大为字符串型,最大长度127字节,不允许空值,无默认值,用于保存Email,保存文本应为Email格式

我们在实体层当中的定义为:

privatenew

 
[Field("EmailAddress"), Validate("\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public FString EmailAddess
{
get
       {
       return
       }
       set
       {
              this.mEmailAddess.Replace(value);
       }
}

在上述声明中,自定义属性Field("EmailAddress")表明了该Property映射到的数据列EmailAddress,而Validate("\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")则是验证的正则表达式(该项设定非必需),这样,在以后的开发中,凡是对该字段传入的值不与上述正则匹配,DataQuicker将自动抛出异常。


 

另外,在DataQuicker加载时,会读取DB Schema,将EmailAddess数据列信息保存在mEmailAddress FString字段对象中,根据最初我们对EmailAddress数据列的描述,mEmailAddress对象的内部结构为:

mEmailAddress.AllowNull = false;
mEmailAddress.HasDefaultValue = false;
mEmailAddress.Size = 127;
mEmailAddress.MappingName = “EmailAddess”;
mEmailAddress.IsAutoIncrease = false;
mEmailAddress.IsPrimaryKey = false;
mEmailAddress.Validator = {StringValidator对象};


 

这样,在我们的开发中,下列情况都将被DQ自动抛出异常:

User user = User.CreateInstance();

user.EmailAddress.Value = “asasdad”;   //邮件地址不符合正则表达式X@X.X

user.EmailAddress.Value = “超过127长度的字符串”;  //字符串超长

在我们Update/Insert user时,DQ会去自动检测user.EmailAddress值的有效性,包括是否为空、长度限制、正则表达式限制等,如果有效性不符合,DQ会抛出异常


 

需要说明的是,在Property EmailAddress的set方法中,并没有直接的进行替换

this.mEmailAddess = value;

而是使用this.mEmailAddess.Replace(value);方法,这样为了提高程序的安全性,试想一下,如果我们把其他一个FString类型的对象赋值给EmailAddress属性,如果前者的话就会直接通过(除非我们使用AOP进行拦截),但是在后者中,Replace中做了有效性验证。


 

上面对于FString有StringValidator验证类,对于其他字段类型的也有相应的验证类,它们都继承于Validator抽象基类,被聚集在字段的抽象基类FieldMapping中,参考UML图。但是所有的字段的验证声明都使用Validate自定义属性,它的构造函数原型是

publicparams object[] candidator)

这里,会有Box/Unbox的性能损伤,但是这种损失可以提高程序的灵活性,我想是值得的。


 

另外,在最近一次的改动中,我去掉了Aspect#,而改用了传统的代码实现DataQuicker的实体层部分,因为:

1, 在我的NUnit测试中,使用AOP的构造对象运行时间会比不使用高出10%以上;

2, 我在序列化AOP对象时遇到了问题,这是致命的,试想实体对象无法序列化的话会将多少应用拒之门外