实体控制层
解决和O-R Map的问题,需要考虑的就是实体类的持久性问题了,也就是同数据库的交互问题。实体控制层用于控制数据的基本操作,如增加、修改、删除、查询等,同时为业务规则层提供数据服务。
实体控制层的类实现IEntityDAO接口。这个接口定义了实现数据操纵的主要必要方法,包括增加、修改、删除和查找。IEntityDAO的定义如下:
可以看到,这个接口同J2EE中EntityBean的接口定义很象,实际上,我们也是参考了EntityBean的解决方案。
下面是一个Product的DAO类的例子:
同数据实体层相结合,这两部分实现了应用服务层同数据库的交互。这两个部分结合,完成了类似于J2EE中EntityBean的功能。
采用数据实体和实体控制分开的设计方法,具有以下优点:
· 避免了J2EE体系中操纵EntityBean系统资源消耗大,效率低下的缺陷。
· 解决了J2EE体系中使用EntityBean传输数据时开销大,过程复杂、效率低的缺陷。
· 可以单独修改实体结构和对实体数据的操纵,使得系统更加灵活
· 数据实体的XML定义文件和实体控制层的类可以通过工具自动生成,减轻开发工作量。
数据访问层
为了为实体控制层提供对数据库操作的服务,我们设计了这个部分。这个层次通常执行以下一些操作:
· 连接数据库
· 执行数据库操作
· 查询数据库,返回结果
· 维护数据库连接缓存
· 数据库事务调用
为了统一对数据的访问方式,我们在设计的时候,在框架的类库中包含了数据访问服务,封装了常用的对各种数据库的操作,可以访问不同类型的数据库,这样,在具体软件系统开发的时候,可以不用考虑同数据库的连接等问题,也使得应用系统在更换数据库时,不用修改原有的代码,大大简化了开发和部署工作。数据访问服务还维护数据库连接缓存,提高系统性能,以及对数据库事务调用的服务。
数据访问服务在核心类库中主要通过DBCommon类来提供对数据访问功能调用的服务。DBCommon的使用方法在上面的ProductEntityDAO中可以看出一二。更多的可以看看Demo工程中的使用。
业务规则层
业务规则层需要完成的功能是各种业务规则和逻辑的实现。业务规则完成如客户帐户和书籍订单的验证这样的任务。这是整个应用系统中最为复杂的部分,没有太多的规律可循。但是,我们在完成上面的工作后,对于这个部分的开发,也可以起到一定的简化的工作。这从下面的例子可以看到。
业务规则层的设计通常需要进行很好的建模工作。业务规则的建模,一般采用UML来进行。可以使用UML的序列图、状态图、活动图等来为业务规则建模。这个部分的工作,通常通过一系列的类之间的交互来完成。
业务规则通常要求系统能够支持事务处理(Transaction)。在这个地方,.Net提供了很方便的调用Windows Transaction Server的手段。关于这个部分的内容,各位自己阅读MSDN就非常清楚了,这里就不做详细的介绍了。
例如,在一个库存系统的入库单入库操作中,除了需要保存入库单外,在这个之前,还必须对入库单涉及的产品的数量进行修改,其代码通常如下(使用了事务处理):
public void StoreIntoWarehouse(EntityData IndepotForm)
{
DataTable tbl=IndepotForm.Tables["InDepotFormDetail"];
try
{
ProductEntityDAO ped=new ProductEntityDAO();
for(int i=0;i<tbl.Rows.Count;i++)
{
DataRow formdetail=tbl.Rows[i];
string productID=formdetail["ProductID"].ToString();
decimal
inCount=(decimal)formdetail["InCount"];
EntityData product=ped.FindByPrimaryKey(productID);
DataRow productRow=product.GetRecord("Product");
productRow["CurrentCount"]=(decimal)productRow["CurrentCount"]+inCount;
ped.UpdateEntity(product);
}
ped.Dispose();
InDepotFormEntityDAO inDepotForm=new
InDepotFormEntityDAO();
inDepotForm.InsertEntity(IndepotForm);
IndepotForm.Dispose();
ContextUtil.SetComplete();
}
catch(Exception ee)
{
ContextUtil.SetAbort();
throw ee;
}
}
解决和O-R Map的问题,需要考虑的就是实体类的持久性问题了,也就是同数据库的交互问题。实体控制层用于控制数据的基本操作,如增加、修改、删除、查询等,同时为业务规则层提供数据服务。
实体控制层的类实现IEntityDAO接口。这个接口定义了实现数据操纵的主要必要方法,包括增加、修改、删除和查找。IEntityDAO的定义如下:
public interface IEntityDAO : IDisposable { void InsertEntity(EntityData entity); void UpdateEntity(EntityData entity); void DeleteEntity(EntityData entity); EntityData FindByPrimaryKey(object strKeyValue); } |
可以看到,这个接口同J2EE中EntityBean的接口定义很象,实际上,我们也是参考了EntityBean的解决方案。
下面是一个Product的DAO类的例子:
public class ProductEntityDAO: IEntityDAO { private DBCommon db; //这是数据库访问的类 public ProductEntityDAO() { db=new DBCommon(); db.Open(); } public ProductEntityDAO(DBCommon cdb) { this.db=cdb; } // 插入一个实体 public void InsertEntity(EntityData entity) { CheckData(entity); db.BeginTrans(); try { foreach(DataRow row in entity.Tables["Product"].Rows) db.exeSql(row,SqlManager.GetSqlStruct("Product","InsertProduct")); db.CommitTrans(); } catch(Exception e) { db.RollbackTrans(); throw e; } } //修改一个实体类 public void UpdateEntity(EntityData entity) { CheckData(entity); db.BeginTrans(); try { foreach(DataRow row in entity.Tables["Product"].Rows) if(row.RowState!=DataRowState.Unchanged) db.exeSql(row,SqlManager.GetSqlStruct("Product","UpdateProduct")); db.CommitTrans(); } catch(Exception e) { db.RollbackTrans(); throw e; } } //删除一个实体类 public void DeleteEntity(EntityData entity) { CheckData(entity); db.BeginTrans(); try { foreach(DataRow row in entity.Tables["Product"].Rows) db.exeSql(row,SqlManager.GetSqlStruct("Product","DeleteProduct")); db.CommitTrans(); } catch(Exception e) { db.RollbackTrans(); throw e; } } //查找实体类 public EntityData FindByPrimaryKey(object KeyValue) { EntityData entity=new EntityData("Product"); SqlStruct sqlProduct=SqlManager.GetSqlStruct("Product","SelectByIDProduct"); db.FillEntity(sqlProduct.SqlString,sqlProduct.ParamsList[0], KeyValue,entity,"Product"); return entity; } public EntityData FindAllProduct() { EntityData entity=new EntityData("Product"); SqlStruct sqlProduct=SqlManager.GetSqlStruct("Product","FindAllProduct"); db.FillEntity(sqlProduct.SqlString,null,null,entity,"Product"); return entity; } // 校验数据数据输入的有效性 private void CheckData(EntityData entity) { if(entity.Tables["Product"].Rows[0]["ProductID"].ToString().Length>40) throw new ErrorClassPropertyException ("Property ProductID should be less than 40 characters"); } public void Dispose() { Dispose(true); GC.SuppressFinalize(true); } protected virtual void Dispose(bool disposing) { if (! disposing) return; // we're being collected, so let theGC take care of this object db.Close(); } } |
同数据实体层相结合,这两部分实现了应用服务层同数据库的交互。这两个部分结合,完成了类似于J2EE中EntityBean的功能。
采用数据实体和实体控制分开的设计方法,具有以下优点:
· 避免了J2EE体系中操纵EntityBean系统资源消耗大,效率低下的缺陷。
· 解决了J2EE体系中使用EntityBean传输数据时开销大,过程复杂、效率低的缺陷。
· 可以单独修改实体结构和对实体数据的操纵,使得系统更加灵活
· 数据实体的XML定义文件和实体控制层的类可以通过工具自动生成,减轻开发工作量。
数据访问层
为了为实体控制层提供对数据库操作的服务,我们设计了这个部分。这个层次通常执行以下一些操作:
· 连接数据库
· 执行数据库操作
· 查询数据库,返回结果
· 维护数据库连接缓存
· 数据库事务调用
为了统一对数据的访问方式,我们在设计的时候,在框架的类库中包含了数据访问服务,封装了常用的对各种数据库的操作,可以访问不同类型的数据库,这样,在具体软件系统开发的时候,可以不用考虑同数据库的连接等问题,也使得应用系统在更换数据库时,不用修改原有的代码,大大简化了开发和部署工作。数据访问服务还维护数据库连接缓存,提高系统性能,以及对数据库事务调用的服务。
数据访问服务在核心类库中主要通过DBCommon类来提供对数据访问功能调用的服务。DBCommon的使用方法在上面的ProductEntityDAO中可以看出一二。更多的可以看看Demo工程中的使用。
业务规则层
业务规则层需要完成的功能是各种业务规则和逻辑的实现。业务规则完成如客户帐户和书籍订单的验证这样的任务。这是整个应用系统中最为复杂的部分,没有太多的规律可循。但是,我们在完成上面的工作后,对于这个部分的开发,也可以起到一定的简化的工作。这从下面的例子可以看到。
业务规则层的设计通常需要进行很好的建模工作。业务规则的建模,一般采用UML来进行。可以使用UML的序列图、状态图、活动图等来为业务规则建模。这个部分的工作,通常通过一系列的类之间的交互来完成。
业务规则通常要求系统能够支持事务处理(Transaction)。在这个地方,.Net提供了很方便的调用Windows Transaction Server的手段。关于这个部分的内容,各位自己阅读MSDN就非常清楚了,这里就不做详细的介绍了。
例如,在一个库存系统的入库单入库操作中,除了需要保存入库单外,在这个之前,还必须对入库单涉及的产品的数量进行修改,其代码通常如下(使用了事务处理):
public void StoreIntoWarehouse(EntityData IndepotForm)
{
DataTable tbl=IndepotForm.Tables["InDepotFormDetail"];
try
{
ProductEntityDAO ped=new ProductEntityDAO();
for(int i=0;i<tbl.Rows.Count;i++)
{
DataRow formdetail=tbl.Rows[i];
string productID=formdetail["ProductID"].ToString();
decimal
inCount=(decimal)formdetail["InCount"];
EntityData product=ped.FindByPrimaryKey(productID);
DataRow productRow=product.GetRecord("Product");
productRow["CurrentCount"]=(decimal)productRow["CurrentCount"]+inCount;
ped.UpdateEntity(product);
}
ped.Dispose();
InDepotFormEntityDAO inDepotForm=new
InDepotFormEntityDAO();
inDepotForm.InsertEntity(IndepotForm);
IndepotForm.Dispose();
ContextUtil.SetComplete();
}
catch(Exception ee)
{
ContextUtil.SetAbort();
throw ee;
}
}