1 使用说明
NFramework设计之初就从编码人员的角度来考虑,最终的目的也是为编码人员服务,因此,代码的简洁性与易用性成为严格的设计标准。NFramework充分遵循了“接口隔离原则”,使用户代码不必再依赖于接口,这样您从IDE环境就可以快速的定位到具体的方法体,避免了“转到定义”带来的烦恼。
NFramework建议用户使用三层架构,即表现层、业务逻辑层、数据访问层。当然如何分层以及各种分层方式的优缺点这里不再讨论,下面将只说明基于三层架构的应用程序使用NFramework的具体方法。
1.1 持久化实体类编码说明
NFramework实现了一个轻量级的ORM,因此你要做的第一件事就是定义持久化类,当然你完全可以不用持久化类,但使用持久化类可使你的代码达到最大的简化,并且性能也是最稳定的。NFramework要求在定义持久化类的时候同时设置与数据源的映射,即通过扩展元数据的方式设置实体属性名所对应的字段名以及其它一些相关属性。你可以使用ORMappginAttribute类来设置这些属性,但这种方式产生的一个问题是,对于数据表字段较多的情况下,逐个设置每个属性是很繁琐和费时的事情!别担心NFramework已经替你考虑了这种情况,利用NFramework提供的代码生成器,你只需要选择要生成实体类的数据表,代码生成器即可为你生成所有相关的持久化实体类的代码,复杂的事情也变得简单了。下面我们以实际的代码片断来说明实体类的编写方法。
首先,实体类需要从BaseEntity基类继承,BaseEntity类是框架的重要类之一。利用BaseEntity类中提供的方法可以方便的获取实体与数据表的影射关系,比如说映射的表名、某个属性所对应的字段名等等。不过,BaseEntity最重要以及最常用的功能则是实体与DataTable之间的转换。只需调用一个方法就可以将实体转换成DataTable,或者从DataTable转换成实体,而这一切不需要再编写额外的其它代码,从而大大减少了编码的工作量。实体类详细代码参考如下:
///<summary>
///用户实体类,从BaseEntity类继承
/// ORMapingAttribute类用于标识实体与数据表之间的映射关系
///</summary>
[Serializable]
[ORMaping(TableName="t_system_user")] // 使用ORMappgin设置实体映射的表名
public class UserEntity : BaseEntity
{
// 用户ID
private string m_user_id;
// 用户姓名
private string m_user_name;
///<summary>
///用户ID
///</summary> [ORMaping(FieldName="user_id",IsPK=true,IsSelected=true,IsInserted=true,IsUpdated=false)] // 使用ORMapping设置属性映射的字段名
public string UserID
{
get {return m_user_id;}
set {m_user_id = value;}
}
///<summary>
///用户姓名
///</summary> [ORMaping(FieldName="user_name",IsPK=false,IsSelected=true,IsInserted=true,IsUpdated=true)]// 使用ORMapping设置属性映射的字段名
public string UserName
{
get {return m_user_name;}
set {m_user_name = value;}
}
}
通过以上的设置,我们就可以使用如下方法方便的实现实体与DataTable之间的转换。举例来说:
l 将实体转换成DataTable
UserEntity user = new UserEntity();
DataTable dt = user.ConvertTo(); // 一个方法就可以转换了
l 将DataTable转换成实体
DataTable dt = new DataTable();
UserEntity user = new UserEntity();
user.ConvertFrom(dt); // 一个方法就可以转换了,也可以使用user.Parse(dt)方法
这里要说明的是BaseEntity类提供了两个方法从DataTable转换成实体,即ConvertFrom和Parse。这两个方法都可以实现转换的功能,但它们之间有什么区别呢?
我们知道,利用SQL语句返回查询结果时通常是一个DataTable形式,而DataTable中的列名是根据SQL语句中的查询列名来定义的,因此这就产生了DataTable中的列如何与实体属性值匹配的问题。ConvertFrom方法通过获取实体属性映射的字段名,进而在DataTable中查找同名的列,一旦找到同名列,则会使用此列值作为实体属性值的数据源。Parse方法和ConvertFrom相同,只不过Parse方法使用实体属性名的字符串形式在DataTable中查找,然后再进行匹配。
另外要说明的就是ConvertTo方法,在使用这个方法将实体转换成DataTable时,会使用实体属性名称的字符串形式来创建DataTable列,这样在将DataTable绑定到DataGrid时就可以直接使用属性名称了,而不用再关心数据库中的表结构,即使用表的字段名改变了也只需要改动实体属性名的映射值就可以了,对现有代码无任何影响,这充分体现了ORM的思想,因此ConvertTo方法是非常重要方法,尽管它会带来一些性能上的开销,但在屏蔽数据库方面却有着非常重要的作用。
1.2 业务逻辑类编码说明
业务层通常是系统的核心层,用户所有的业务逻辑均在于此,因此保证用户业务的完整性是非常重要的。以基于数据访问的应用程序中,我们通常使用事务来实现业务数据完整性。NFramework对业务层的支持主要体现的事务的处理方面,业务层代码可以从Transaction类继承,以实现自动事务,或者从TransactionScope类继承以实现分布式事务。无论从哪一个基类继承,其实现代码都是非常简单的。下面给出实际的示例代码:
l 基于连接事务的使用方法示例:
///<summary>
///新增用户的业务逻辑类,从Transaction类继承
///</summary>
public class UserBLL : Transaction
{
///<summary>
///新增
///</summary>
///<param name="user">实体</param>
///<returns>主键ID</returns>
public string Insert(UserEntity user)
{
UserDAL dal = new UserDAL();
try
{
// 开始事务
dal.SyncTrans(this.BeginTransaction());
dal.InsertEntity(user);
// 递交事务
this.Commit();
}
catch (Exception ex)
{
// 回滚事务
this.Rollback();
ErrorHandler.HandleError(ex);
}
return user.UserID;
}
}
l 基于分布式事务的使用方法示例:
///<summary>
///新增用户的业务逻辑类,从TransactionScope类继承。此功能需要COM+的支持
///</summary>
public class UserBLL : TransactionScope
{
///<summary>
///新增
///</summary>
///<param name="user">实体</param>
///<returns>主键ID</returns>
public string Insert(UserEntity user)
{
UserDAL dal = new UserDAL();
try
{
dal.InsertEntity(user);
// 递交事务,不再需要显示的开始事务,NFramework会自动处理
this.CompleteTx();
}
catch (Exception ex)
{
// 回滚事务
this.AbortTx();
ErrorHandler.HandleError(ex);
}
return user.UserID;
}
}
1.3 数据访问类编码说明
数据访问层处于NFramework的最底层,是实现与实际的数据库打交道的地方。NFramework对数据访问层做了限制,即强制其继承Access类,以使用继承的DBUtil属性实现对数据库的访问。另外Access类本身提供了相当多的数据访问方法,用户完全可用Access类的方法实现对数据库的访问,而不需要再写麻烦的数据访问方法。示例代码如下:
///<summary>
///数据访问类,必须继承Access类
///</summary>
internal class UserDAL : Access
{
public void AddUser(UserEntity user)
{
// 生成自动缓存的SQL语句
string sqlCommand = SqlBuilder.BuildSqlInsert(user);
// 使用DBUtil方法,不需要考虑事务、连接等问题,NFramework 会自动处理
DBUtil.ExecuteNonQuery(sqlCommand);
}
}
1.4 特殊情况的处理
以上列举了大多数情况下的数据访问方法的示例,下面将说明一些特殊情况的处理。
1.4.1 不使用事务进行数据访问
如果不想使用事务,则不需要使用业务层(BLL)中的BeginTransaction方法,直接使用数据访问层(DAL)层中的DBUtil对象即可。NFramework框架会自动打开到数据库的连接,并且在执行完之后,自动关闭连接。通常建议对新增、修改、删除等需要更改数据的方法使用事务,而对查询等获取数据的方法不强制要求使用事务,但通过使用事务可以保证你写的业务逻辑代码可以被别人“放心”的使用,而不会出现数据不同步的问题。
1.4.2 访问不同数据库的处理
NFramework框架默认的数据库连接只有一个,即在web.config中配置的数据库连接。因此,如果想操作其它的数据库,则需要使用Access类的GetDBUtil方法来获取数据访问对象。GetDBUtil方法有两个参数,分别是数据库类型和数据库连接字符串,并且返回一个IDBUtil接口用于实现对数据库的抽象访问。需要注意的是,GetDBUtil方法并没有提供事务的处理功能,其实际上只是根据数据库连接字符串打开一个连接,并且使用完成之后自动关闭连接。因此,如果对于这种情况也需要使用事务的话,请使用分布式事务。
1.4.3 分布式事务的处理
对事务的处理,我们有三种选择:
一、基于数据库级别的事务,通常是在存贮过程中显示的定义事务,但这种方式的缺点是事务控制的范围过于狭窄,并且最重要的是很容易将业务逻辑耦合到SQL语句的级别,这不但增加编码的工作量,而且会对系统的架构产生不良影响,同时也不利于系统的升级与维护,但其优点也很明显,即其事务控制的效率是最高的。
二、基于ADO.NET Connection级别的事务,这种方式的事务解决了SQL语句耦合的问题,使用户代码可以专注于业务逻辑的处理,同时ADO.NET的事务使用简单、方便,因此多数应用程序都是采用这种方式来处理事务,但其缺点是无法处理不同数据库间的事务,一旦应用程序有这样的需求,则ADO.NET就显得无能为力了。并且由于其做事务控制时需要与数据库之间有一个往返的通信过程,所以其效率上要差于在数据库级别的事务。
三、基于COM+的分布式事务,COM+提供了自动事务处理的功能,这为我们的程序提供了很大方便,实现分布式事务变得很简单。NFramework框架提供了用于分布式事务的基类TransactionScope,用户代码只要从这个基类继承就可利用COM+实现分布式事务。不过其相对麻烦的是,必须将程序集部署到COM+环境中,这对于系统的安装部署方面显得有些繁琐。