[原创]我的ORM: 开发自己的Data Access Application Block - Part I

经常在网上看到对ORM的讨论沸沸扬扬,我也来凑个热闹,谈谈我写的一个ORM。最近在做一项工作,把我们经常用到的一些业务逻辑抽象出来,写成一个个的Application Block,使之可以运用到不同的Application中,比如Data Access,Messaging,Auditing,Data binding等等。现在先做一个Data access application block。由于时间仓促,没有进行什么优化和较多的测试,大家不必深究我所提供的Code ,我只希望为大家的ORM提供另一种想法。

这个application block应达到的目的:

  • 封装所有的Data Access操作。
  • 适合主流的DBMS:SQL Server(2000和2005),Oracle(9i和10g),DB2。
  • 尽量简化Developer的操作和提供最大的灵活性,在Data Retrieval方面,只要指定SQL或者Stored Procedure和相应的参数;在Data Update方面,既可以直接调用SQL和Stored Procedure,还可以把包含多个相互关联Data Table的Dataset通过一次调用实现数据的更新。此外,可以自由地选择使用SQL还是Stored procedure;可以使用Commander builder生成Command或者使用基于Mapped stored procedure生成的Command进行数据更新。
  • 实现泛型编程,使使用该AppBlock的代码能够适合所有的数据库。
  • 实现Transaction。
  • 提供可配置性,包括不同数据库的配置,不同Data Mapping的配置等等。

下面是该AppBlock使用到的Entity:
[原创]我的ORM: 开发自己的Data Access Application Block - Part I

  • Database:Abstract Class,封装了绝大部分和具体数据库无关的Data Access操作逻辑。通过两个Mapping:IDbParameterNameMapping和IStoredProcedureNameMapping,实现Dataset和Db的一个映射。比如Dataset中Data table name和Stored procedure name的Mapping,Data table中Field和Stored procedure中参数名的Mapping。这两个Mapping是可以配置的,你只需要实现提供的Interface编写适合你的Mapping provider就可以了。
  • SqlDatabase:封装基于SQL Server 的操作。ADO.NET 2.0在1.0的基础上作了很大的改善,主要的增加的大量的基类,为我们进行泛型编程,编写和具体Db无关的代码变得异常容易。所以我们把大多数Data Access的操作可以封装在Abstract Database类中,SqlDatabase中的内容实际上是很少的。
  • OracleDatabase:封装基于Oracle的操作。
  • IDbParameterNameMapping和IStoredProcedureNameMapping:我想大家都是这样的感受,实现ORM的本质就是实现内存中的数据(主要是Dataset)和数据库的一个映射。在Dataset和数据库中的Table相互Mapping方面,我觉得没有必要采用特殊的Mapping,直接和简单易行的就是Table和Dataset中的Data Table完全匹配(table name 和field name完全匹配)。所以重要的是实现Dataset和Stored procedure的Mapping:Table Name如何与进行Insert,Update,Delete的Stored procedure name匹配,不同Version(original & current)的Field如何与Stored procedure的Parameter name 匹配。而这样一个匹配应该是可配置的,因为每个Application在数据库设计时的命名都有各自的要求,所以我在这里采用的Provider的设计模式。用户可以实现这两个Interface编写适合自己的Mapping provider,通过我提供的Configuration block很容易地完成配置。同时,我写了一个默认的,简单的Mapping:SimpleDbParameterNameMapping和SimpleStoredProcedureNameMapping。

有一点需要补充的是,要实现上面的Mapping,对Stored Procedure的命名有较高的要求,手工编写的方式已经不能适合我们的要求,所以我们需要一个生成Stored procedure的Generator,这个Generator也使用这两个可配置的Mapping接口。

  • DatabaseFactory: 一个静态的类,根据配置的信息创建你需要的具体的Database对象,实现泛型化编程。
  • DataAccessConfigurationCollection,DataAccessConfigurationElement,DataAccessConfigurationSection 三个Configuration的Class。

为了使大家清楚地看出这个Application block所有的操作,我把所有的操作封装在一个IDatabase的interface中,不过需要注意的是,我采用的是基于Abstract class的编程,而不是基于Interface的编程,相信大家对这两种方式讨论得已经碰倒的太多了,孰优孰劣我就不想对说了。这个IDatabase 接口,只是展示所有Operation之用,并没有在我的代码中用到。


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->namespaceArtech.ApplicationBlock.DataAccess
{
publicinterfaceIDatabase
{
FillaSystem.Data.DataSetwithretrieveddata.#regionFillaSystem.Data.DataSetwithretrieveddata.
voidFillDataSet(DataSetdataInfo,stringcommandText,IDictionary<string,object>parameters);

voidFillDataSet(DataSetdataInfo,stringtableName,stringcommandText,IDictionary<string,object>parameters);

voidFillDataSet(DataSetdataInfo,stringtableName,CommandTypecommandType,stringcommandText,IDictionary<string,object>parameters);

#endregion


Savethechangeddatawhichisstoredinadatasetintodatabase.#regionSavethechangeddatawhichisstoredinadatasetintodatabase.
voidUpdateData(DataSetdataInfo);

voidUpdateData(DataTabletable);

voidUpdateData(DataTabletable,stringinsertCommandText,stringupdateCommandText,stringdeleteCommandText,
Dictionary
<string,object>insertParameters,Dictionary<string,object>updateParameters,Dictionary<string,object>deleteParameters);

voidUpdateData(DataTabletable,CommandTypecommandType,stringinsertCommandText,stringupdateCommandText,stringdeleteCommandText,
Dictionary
<string,object>insertParameters,Dictionary<string,object>updateParameters,Dictionary<string,object>deleteParameters);

voidUpdateData(DataTabletable,DbCommandinsertCommand,DbCommandupdateCommand,DbCommanddeleteCommand);

#endregion


Executeacommandandreturntheaffectrowcount.#regionExecuteacommandandreturntheaffectrowcount.
intExecuteNonQuery(CommandTypecommandType,stringcommandText,Dictionary<string,object>inputParameters,Dictionary<string,object>outputParameters);

intExecuteNonQuery(CommandTypecommandType,stringcommandText,Dictionary<string,object>inputParameters);

intExecuteNonQuery(stringcommandText,Dictionary<string,object>inputParameters,Dictionary<string,object>outputParameters);

intExecuteNonQuery(stringcommandText,Dictionary<string,object>inputParameters);
#endregion


Executeacommandandreturnthedataintheformofdatareader.#regionExecuteacommandandreturnthedataintheformofdatareader.
DbDataReaderExecuteReader(CommandTypecommandType,
stringcommandText,Dictionary<string,object>inputParameters,Dictionary<string,object>outputParameters);

DbDataReaderExecuteReader(CommandTypecommandType,
stringcommandText,Dictionary<string,object>inputParameters);

DbDataReaderExecuteReader(
stringcommandText,Dictionary<string,object>inputParameters,Dictionary<string,object>outputParameters);

DbDataReaderExecuteReader(
stringcommandText,Dictionary<string,object>inputParameters);
#endregion


Executeacommandandreturnascalarvalue.#regionExecuteacommandandreturnascalarvalue.

objectExecuteScalar(CommandTypecommandType,stringcommandText,Dictionary<string,object>inputParameters,Dictionary<string,object>outputParameters);

objectExecuteScalar(CommandTypecommandType,stringcommandText,Dictionary<string,object>inputParameters);

objectExecuteScalar(stringcommandText,Dictionary<string,object>inputParameters,Dictionary<string,object>outputParameters);

objectExecuteScalar(stringcommandText,Dictionary<string,object>inputParameters);
#endregion


Transactionbasedoperation#regionTransactionbasedoperation
voidBeginTransaction();
voidCommit();
voidRollBack();
#endregion

}

}

这个列表和大部分ORM没有什么太大的区别,大家已经司空见惯,实现起来也不会有什么太大的困难。对于大部分操作,我不会做详细的介绍。接下来我们来简要地看看这样一个AppBlock是如何实现的。
1. Data Mapping

我们首先来看看Data Mapping:实现Dataset中Table name和Stored Procedure Name的Mapping,以及Dataset 中的Field 和Stored procedure的参Parametername的Mapping。

IDbParameterNameMapping


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;

namespaceArtech.ApplicationBlock.DataMapping
{
/**////<summary>
///IStoredProcedureNameMappingdefinesthemappingbetweenthedatatablenameandthenameofstoredprocedurestoperforminsertion,modificationanddeletionoperation.
///</summary>

publicinterfaceIStoredProcedureNameMapping
{
/**////<summary>
///Getthenameofstoredproceduretoperformseletionoperation.
///</summary>
///<paramname="tableName">Thenameofthedatabasetable.</param>
///<returns>Thenameofstoredproceduretoperformseletionoperation</returns>

stringGetSelectStoredProcedureName(stringtableName);

/**////<summary>
///Getthenameofstoredproceduretoperforminsertoperation.
///</summary>
///<paramname="tableName">Thenameofthedatabasetable.</param>
///<returns>Thenameofstoredproceduretoperforminsertionoperation</returns>

stringGetInsertStoredProcedureName(stringtableName);

/**////<summary>
///Getthenameofstoredproceduretoperformmodificationoperation.
///</summary>
///<paramname="tableName">Thenameofthedatabasetable.</param>
///<returns>Thenameofstoredproceduretoperformmodificationoperation</returns>

stringGetModifyStoredProcedureName(stringtableName);

/**////<summary>
///Getthenameofstoredproceduretoperformdeletionoperation.
///</summary>
///<paramname="tableName">Thenameofthedatabasetable.</param>
///<returns>Thenameofstoredproceduretoperformdeletionoperation</returns>

stringGetDeleteStoredProcedureName(stringtableName);
}

}

IDbParameterNameMapping


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Data;
namespaceArtech.ApplicationBlock.DataMapping
{
/**////<summary>
///IDbParameterNameMappingdefinethedefultmappingbetweenthesourcecolumnnameandtheparameternameofthecorrespondingstoredprocedure.
///</summary>

publicinterfaceIDbParameterNameMapping
{
/**////<summary>
///Getthesourcecolumnnamebasedontheparameternameoftherelatedstoredprocedure.
///</summary>
///<paramname="patameterName">Theparameternameofthecorrespondingstoredprocedure.</param>
///<returns>Thesourcecolumnnamecorrespondingtotheparametername.</returns>

stringGetSourceCoulmnName(stringpatameterName);

/**////<summary>
///Getthesourceparameternamebasedonthesourcecolumnname.
///</summary>
///<paramname="columnName">Thesourcecolumnnamecorrespondingtotheparametername.</param>
///<paramname="rowVersion">Thedatarowversionofthesourcesolumnconresspondingtotheparameter.</param>
///<returns>Theparameternameofthecorrespondingstoredprocedure.</returns>

stringGetParameterName(stringcolumnName,DataRowVersionrowVersion);
}

}

这两个Mapping主要用在通过Dataset跟新数据库的场景,利用IDbParameterNameMapping,我们通过Dataset中各个Table name获得对它进行Insert,Update,Delete操作的Stored procedure的name。利用IDbParameterNameMapping,我们可以为Stored procedure的Parameter指定对应的Source field.

注:GetParameterName方法实际上是不需要的,我把使用在另一个AppBlock中。

接下来我们来写两个实现了上面连个Interface的默认的mapping:SimpleStoredProcedureNameMapping和SimpleDbParameterNameMapping。他实际上实现了这样的Mapping:比如Table name为T_ABC_DEF(我经常用的命名方式:以T开头代表Table,名称大写并一下划线连接),那么对应的Stored procedure name分别为:sp_abc_def_s(Select), sp_abc_def_i(Insert), sp_abc_def_u(Update), sp_abc_def_d(delete)。如果Field name为ABC_123,那么对于Original version的Parameter name为o_abc_123(o代表Original),Current version的Parameter name为p_abc_123(p代表一般意义的Parameter)。


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;

namespaceArtech.ApplicationBlock.DataMapping
{
/**////<summary>
///IStoredProcedureNameMappingdefinesthemappingbetweenthedatatablenameandthenameofstoredprocedurestoperforminsertion,modificationanddeletionoperation.
///</summary>

publicclassSimpleStoredProcedureNameMapping:IStoredProcedureNameMapping
{
IStoredProcedureNameMappingMembers#regionIStoredProcedureNameMappingMembers
/**////<summary>
///Getthenameofstoredproceduretoperformselectionoperation.
///</summary>
///<paramname="tableName">Thenameofthedatabasetable.</param>
///<returns>Thenameofstoredproceduretoperformselectionoperation</returns>

publicstringGetSelectStoredProcedureName(stringtableName)
{
//T_ABC_DEF=>sp_abc_def_s
returnstring.Format("sp_{0}_s",tableName.Substring(2,tableName.Length-2).ToLower());
}


/**/Codehighlighter1_993_1270_Op
分享到:
评论

你可能感兴趣的:(sql,sql,编程,server,orm,Access)