开发中的DataQuicker
DataQuicker是一款O/R Mapping工具,意在完成实体层开发的智能化。我们知道,在大部分的系统中,我们都会投入大量的时间和人力来进行实体层编码,包括SQL拼写、数据有效性检查以及实体层的设计等,硬编码的SQL或者使用存储过程会在一定提高应用程序的性能,但是却使程序和数据库的耦合性上升,使后期维护或者中期的变更变成一场灾难。
市面上有很多的ORM,我有研究过NHiberate和Gentle.NET。对于NHiberate,它的使用相对麻烦,并且当我在实体层的使用时上减小工作量的,同时Domain Modal的设计却会花费我们的大量时间,并且钻研NHiberate也花费我们很多的时间,在我以前的公司里,一个JAVA Team的伙计就经常发呆般的看着白板思考如何设计Domain。对于Gentle.NET,我个人认为它相对简单,这里是说它能够完成的功能,以及在数据库-应用程序之间的映射上。当然,我这里不是说他们不好,只是适合与否而已。
而DataQuicker相对于Gentle是一款更加OO的ORM,它不仅考虑表实体,它同时封装了字段,不同的数据库字段类型对应DataQuicker中FieldMapping字段抽象类的派生类,比如text-FString,int-FInt,decimal-FDecimal。这样,在系统初始化时,DQ首先会得到数据库的结构Schema,每个表实体的字段对象对应数据库中的字段,它们也会保存表字段的Schema,比如长度、默认值甚至自自己的正则表达式,以便我们在操作中,凡是遇到不合法的输入,它可以不与数据库通信即检查数据的有效性…
DataQuicker的实体层设计非常简单,因为它使用了Aspect#,只需要罗列出表、字段、关联表即可。如示例代码:
using System;
using DataQuicker.Framework;
namespace DataQuicker.UnitTest
{
//Entity属性指定该实体映射的表/试图名称
// Provider属性制定该表对应的DataBase,DataQuicker允许在多个不同数据库连接的支持,比如两个SQL数据库的连接,
//一个是DB1,另一个是DB2。这里,对于DB1/2的定义是在配置文件中进行的
[Entity("reply_message"), Serializable, Provider("DB1")]
public class MessageDetail: TableMapping
{
public MessageDetail(){}
//标注该字段映射的数据库中字段的名称,FPrimaryKey说明该字段是PrimaryKey
[Field("identity")]
public virtual FPrimaryKey ID
{
get
{
return null;
}
}
[Field("datetime"), Alias("reply_datetime")]
public virtual FDateTime ReplyDateTime
{
get
{
return null;
}
}
[Field("author")]
public virtual FString Author
{
get
{
return null;
}
}
[Field("reply_message")]
public virtual FString ReplyMessage
{
get
{
return null;
}
}
//这里是一个关联表,message_ref_id是指当前Detail表中的外键,而Alias("master_message")指定关联的Header在当前字体中将使用表别名
[Association("message_ref_id"), Alias("master_message")]
public virtual MessageHeader MessageHeader
{
get
{
return null;
}
}
//构造对象,DataQuicker中将不使用实体类的构造函数构造对象,而使用静态的CreateInstance方法,每个实体类需要实现该方法
public static MessageDetail CreateInstance()
{
return PersistenceBuilder.Instance.CreateInstance(typeof(MessageDetail)) as MessageDetail;
}
}
}
using System;
using DataQuicker.Framework;
namespace DataQuicker.UnitTest
{
/// <summary>
///这里是上示例中关联的MessageHeader表的定义
/// </summary>
[Entity("message"), Serializable, Provider("DB1")]
public class MessageHeader: TableMapping
{
public MessageHeader()
{
}
[Field("identity")]
public virtual FPrimaryKey ID
{
get
{
return null;
}
}
[Field("author")]
public virtual FString Author
{
get
{
return null;
}
}
[Field("subject")]
public virtual FString Subject
{
get
{
return null;
}
}
[Field("message")]
public virtual FString Message
{
get
{
return null;
}
}
[Field("datetime"), Alias("create_datetime")]
public virtual FDateTime CreateDateTime
{
get
{
return null;
}
}
public static MessageHeader CreateInstance()
{
return PersistenceBuilder.Instance.CreateInstance(typeof(MessageHeader)) as MessageHeader;
}
}
}
通过以上的代码,实体层就设计好了,以下演示我在NUnit中的调用,
[Test]
public void TestSelect()
{
MessageDetail detail = MessageDetail.CreateInstance();
detail.MessageHeader.Author.SetCondition("Eunge");
detail.MessageHeader.Subject.SetCondition("DataQuicker");
detail.AddOrderBy(detail.MessageHeader.Subject);
// SQLAnalyzer只是一个内置的SQLServer语句分析器,其他还是Jet、Oracle等,在实际中,应该是直接调用detail.Select()得到查询结果。这里
//只是为了演示分析出的查询语句
SQLAnalyzer analyzer = new SQLAnalyzer();
string strSQL = this.analyzer.Select(detail, new FieldMapping[]{detail.ID, detail.MessageHeader.Subject}, true, 100);
string strExpect = "SELECT DISTINCT TOP 100 reply_message.[identity], master_message.subject FROM reply_message JOIN message master_message ON reply_message.message_ref_id=master_message.[identity] WHERE (master_message.subject = 'DataQuicker') AND (master_message.author = 'Eunge') ORDER BY master_message.subject";
Assert.AreEqual(strExpect, strSQL);
}
DataQuicker是我第一个Test-Driven驱动的项目,其实由于个人对TD还不能完全掌握,只是在一种揣摩中进行了DataQuicker的开发,现在开发还在进行,最迟3个月内能与广大朋友见面。
到时候还请多多支持和关注才是。