2.1 安装
1.1.1 软件环境
要求已安装如下软件:
1、 安装了.Net Framework 2.0
2、 安装了Visual Studio 2005
3、 如果要访问MSAccess数据库,需安装了MDAC(1.7以上版本)
4、 如果要访问Oracle数据库,需安装了ODP.NET
专业的开发人员一般都知道如何获取和安装MDAC和ODP.NET,所以本文不再累赘。
1.1.2 准备
解压缩AppFramework.DBAccess.rar压缩包,得到如下目录结构:
1.1.3 安装CodeGenPlogin工具
执行Tools/AppFramework.Tools.CodeGenPluginSetup.msi:
按向导提示完成安装。
1.1.4 复制AppFramework到项目目录下
把AppFramework目录复制到您解决方案目录或子目录下任意位置,方便项目引用到程序集。如果解决方案里已经存在AppFramework的其它组件,可以覆盖之。
1.2 添加引用
假设解决方案目录结构如下:
其中AppFramework是将要引用的程序集目录。在IDE解决方案资源管理器项目节点的引用上打开右键菜单,选择添加,浏览到AppFramework目录,选中AppFramework.Data.dll 和 AppFramework.DBAccess.dll,点击“确定”。例如:
1.3 添加配置文件
从安装包里复制Config目录:
粘贴到解决方案里的项目节点下:
完成后,将会看到Config目录下有三个文件:
注意,不要修改Config目录名字和位置,DAOManager类默认从应用程序启动目录的Config子目录下加载DBAccess.config配置文件。
1.3.1 配置DBAccess.config文件
第一、配置文件内容
DBAccess.config作用是集中定义程序中用到的数据库连接串。在IDE解决方案资源管理器双击“DBAccess.config”,看到如下信息:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<DataSource
Name="IMS"
DBType="SQLServer" DBVersion="8.0"
Default="true"
ConnectionString="Data Source=./SQL2005;Initial Catalog=IMS;User ID=IMSUser;Password=12345678" />
</configuration>
其中,
Name:表示数据源标识,可以随意取名,在程序中对应到DBSessionManager.Default.GetSession(string dataSourceName)方法的dataSourceName参数。
DBType:数据库类型标识,只允许为SQLServer, Oracle, Access三种。
DBVersion:数据库的版本号,目前只有DBType为SQLServer才有用,8.0表示SQLServer2005,其他表示SQLServer2005以下的版本。
Default:表示默认的数据库连接,如果设置Default=”true”,则DBSessionManager.Default.GetSession()会默认创建此数据库连接。
ConnectionString:数据库连接串,不同类型的数据库有不同的写法,不同数据库的DataSouce配置可以参考如下:
(1) SQLServer
<DataSource
Name=" IMS "
DBType=" SQLServer "
DBVersion="8.0"
ConnectionString=" Data Source=./SQL2005;Initial Catalog=IMS;User ID=IMSUser;Password=12345678"
/>
(2) Oracle
<DataSource
Name="IMS"
DBType="Oracle"
DBVersion=" 9.0.14 "
ConnectionString="Data Source=IMSDB;User ID=IMSUser;Password=12345678"
/>
(3) Access
<DataSource
Name="IMS"
DBType="Access"
DBVersion=""
ConnectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=IMS.mdb;Jet OLEDB:Database Password=12345678;Persist Security Info=False"
/>
第二、设置文件属性
在IDE解决方案资源管理器选中“DBAccess.config”,在IDE对象属性窗设置“复制到输出目录”为“始终复制”:
1.3.2 配置CodeGenPlugin.config文件
第一、配置文件内容
因为代码生成器要实时连接到数据库获取表结构并生成实体类和访问类与接口,所以CodeGenPlugin.config作用是配置代码生成器使用的数据库连接串、配置生成代码的保存位置。对每个“Xxx.DaoGen”结尾的文件,代码生成器会生成三个文件:
1、 XxxModel.cs:包含各种实体类
2、 XxxDAOInterface.cs:包含各种DAO的接口
3、 XxxDao.cs:包含各种DAO类,实现了XxxDAOInterface.cs里所定义的接口。
默认情况下,这三个文件生成在同一目录下。如果代码用到三层结构,这三个文件应该分属不同的项目,那么可以通过配置“CodeGenPlugin.config”,指定XxxModel.cs、XxxDAOInterface.cs在IDE解决方案资源管理里的保存位置。而XxxDao.cs的生成位置无法配置,只能固定在与Xxx.DaoGen文件同一目录下。
在IDE解决方案资源管理器双击“CodeGenPlugin.config”,看到如下信息:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<DataSource
Name=" IMS "
DBType=" SQLServer "
DBVersion="8.0"
ConnectionString=" Data Source=./SQL2005;Initial Catalog=IMS;User ID=IMSUser;Password=12345678"
/>
<SavePath
Model=" Demo/GeneratedCode"
DAOInterface=" Demo/GenefatedCode"
/>
</configuration>
其中<DataSource>节与DBAccess.config的<DataSource>节语法完全相同。<SavePath>则定义了Model空间下和DAOInterface空间下生成代码的保存路径。路径的格式为:
项目名称/项目目录1/项目目录2/.../项目目录n、
例如本例的:Demo/GeneratedCode
第二、设置文件属性
在IDE解决方案资源管理器选中“CodeGenPlugin.config”,在IDE对象属性窗设置“复制到输出目录”为“不复制”:
1.3.3 添加.DaoGen文件
Xxx.DaoGen文件作用是配置ORMap信息,用于生成数据表实体和对应的DAO类和接口。特别注意:Xxx.DaoGen文件所在的目录下必须有一个CodeGenPlugin.config文件。程序中可以有多个.DaoGen文件,对于每个.DaoGen文件,代码生成器都会生成三个.cs文件:
1、 XxxModel.cs:包含各种实体类
2、 XxxDAOInterface.cs:包含各种DAO的接口
3、 XxxDao.cs:包含各种DAO类,实现了XxxDAOInterface.cs里所定义的接口。
默认情况下,这三个文件生成在同一目录下。如果代码用到三层结构,这三个文件应该分属不同的项目,那么可以通过配置“CodeGenPlugin.config”实现把文件保存到不同的项目下(具体请参考上一节)。
第一、配置文件内容
在例子中,双击IDE解决方案资源管理器上的“Sample.DaoGen”结点打开文件,可以看到如下信息:
<?xml version="1.0" encoding="gb2312" ?>
<Map>
<Head
Namespace="Demo"
Using=""
>
<Description>
Type: Table(默认), View, SQL
TableName: 表或视图名,如果Type为SQL, TableName 表示 SQL 的 Alias
CSharpTableName: 对应到 CSharp 代码里的表名
PrimaryKey: 主键,格式 XX|XX|XX
InheritedTableName: 表示表之间的继承关系,从哪个表继承,其值是某个 MapItem 的 TableName
ReadOnly: false 或 true,true 表示只生成查询方法, 默认:false
</Description>
</Head>
<MapItem TableName="BAS_User" PrimaryKey="ID">
<statement id="GetBasUserList">
<dynamic prepend="dept_id in (select id from BAS_Dept where" append=")" seprator="or">
<isNotNull property="DeptName"> name like #DeptName# </isNotNull>
<isNotNull property="CreatedTimeBegin"> created_time >= #CreatedTimeBegin# </isNotNull>
</dynamic>
</statement>
<statement id="GetBasUserList1">
<dynamic prepend="" seperator="and">
<isNotNull property="ID"><![CDATA[ID < #ID# ]]> </isNotNull>
<isNotNull property="Name">Name like #Name# </isNotNull>
</dynamic>
</statement>
</MapItem>
<MapItem TableName="BAS_Dict" PrimaryKey="ID" />
<MapItem Type="View" TableName="BAS_User_V" PrimaryKey="ID" InheritedTableName="BAS_User" />
<statement id="GetUserAgeAvg">
<!-- 获取(某个部门下)人员的平均年龄 -->
select avg(a.age) from (
select age from bas_user
<dynamic prepend="where dept_id in (" append=")" seperator="and">
<isNotNull property="Names">select id from bas_dept where Name in ($Names$)</isNotNull>
</dynamic>
) a
</statement>
</Map>
DaoGen文件详细的语法请参考下一节。
第二、设置文件属性
在IDE解决方案资源管理器选中“Xxx.DaoGen”,在IDE对象属性窗设置“复制到输出目录”为“不复制”,并把“自定义工具”设置为“CodeGenPlugin”:
注意,一定要事先在CodeGenPlugin.config的<DataSource>节里配置好数据库连接,否则无法连接数据库久无法生成代码,IDE还会报告类似如下错误信息:
1.4 编写.DaoGen文件
1.4.1 生成代码的结构
对于每个Yyy.DaoGen文件,代码生成器都会生成三个.cs文件、三个命名空间:
文件名 | 命名空间 | 类 |
YyyModel.cs | ___.Model | 包含表的元数据类、各种实体类及其接口,例如Yyy、YyyParam、IYyy、IYyyParam、YyyDef; |
YyyDAOInterface.cs | ___.Access.Interface | 包含各种DAO的接口; |
Yyy.cs | ___.Access | 包含各种DAO类和DAOFactory类,实现YyyDAOInterface.cs里所定义的接口。 |
三个文件的依赖关系为:
YyyModel.cs |
AppFramework.DBAccess.dll |
YyyDAOInterface.cs |
AppFramework.Data.dll |
Yyy.cs |
代码生成的类可以直观地表示为下图:
数据库 |
Yyy表 |
Yyy实体
|
YyyParam实体 |
IYyy接口 |
IYyyParam接口 |
YyyDef元数据 |
YyyDAO |
YyyDAOFactory |
IYyyDAO接口 |
Zzz表 |
1.4.2 生成代码的功能
类名 | 功能说明 |
YyyDef元数据
| 提供了Yyy表的名称、Yyy表的字段列表、Yyy表的主键字段名等等定义在.DaoGen中的映射信息,共程序使用,避免了对表名、字段名硬编码。 |
Yyy实体 | 用于映射查询语句返回的记录。 |
YyyParam实体 | 用于提供插入和更新记录的数据。 |
IYyy接口 | 描述Yyy实体的接口,提高代码的集成能力。 |
IYyyParam接口 | 描述YyyParam实体的接口,提高代码的集成能力。 |
IYyyDAO | 描述YyyDAO的接口,提高代码的集成能力。 |
YyyDAO | 封装了Yyy表进行增删改查功能。 |
YyyDAOFactory | 构造YyyDAO的工厂类。 |
1.4.3 最简单的.DaoGen文件
大部分情况下我们都只会用到最简单的几种标签。一个最简单的.DaoGen文件内容如下:
<?xml version="1.0" encoding="gb2312" ?>
<Map>
<Head Namespace="Demo" >
<Description>
一个最简单的.DaoGen文件
</Description>
</Head>
<MapItem TableName="BAS_User" PrimaryKey="ID" />
<MapItem TableName="BAS_Dict" PrimaryKey="ID" />
</Map>
在这个例子里,只需要关注以下几个节点或属性:
节点 | 属性 | 说明 |
Head | Namespace | 制定生成代码的命名空间前缀。例如设置为“Demo”,则将得到三个命名空间: (1) Demo.Model (2) Demo.Access (3) Demo.Access.Interface |
MapItem | TableName | 指定数据库中的表名。例如输入“BAS_User”,则得到BasUser、BasUserDAO、BasUserDAOFactory、BasUserDef、BasUserParam、IBasUser、IBasUserParam、IBasUserDAO等若干个类。 |
| PrimaryKey | 指定主键字段名,一般都是单主键,且默认名字为“ID”。如果表的主键名正好是“ID”,此属性可以不填写。如果有多个字段组成联合主键,字段名间用“|”符号间隔。 |
从上例,我们可以生成BAS_User和BAS_Dict两张表的实体类、DAO类。
1.4.4 代码生成
在IDE解决方案资源管理器.DaoGen文件节点上右键菜单里点击“运行自定义工具”,或在IDE编辑器打开.DaoGen文件再保存,都会触发代码生成器插件生成代码:
根据CodeGenPlugin.config里<SavePath>节点设置,生成的Model和DAOInterface文件被保存到Demo项目的GeneratedCode目录下,而DAO文件则以子项的方式存放在Sample.DaoGen节点下:
(1)Sample.cs:
/*
下列代码由 [代码生成器 V1.0] 生成
时间:2007-08-12 17:57:21
*/
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using AppFramework.Data;
using AppFramework.DBAccess;
using Demo.Model;
using Demo.Access.Interface;
/// <summary>
/// 此命名空间下的类或接口由代码生成器生成
/// </summary>
namespace Demo.Access
{
/// <summary>
/// BAS_User 的数据访问类,处理字段包括:
/// ID,Name,En_Name,Password,Dept_ID,Org_ID,Employee_No,Email,State,Creator_ID,Created_Time,Updated_By,Updated_Time,Age
/// </summary>
public class BasUserDAO : DAOBase, IBasUserDAO
{
……
(2)SampleDAOInterface.cs:
/*
下列代码由 [代码生成器 V1.0] 生成
时间:2007-08-12 17:57:21
*/
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using AppFramework.Data;
using AppFramework.DBAccess;
using Demo.Model;
/// <summary>
/// 此命名空间下的类或接口由代码生成器生成
/// </summary>
namespace Demo.Access.Interface
{
/// <summary>
/// BAS_User 的数据访问类接口,处理字段包括:
/// ID,Name,En_Name,Password,Dept_ID,Org_ID,Employee_No,Email,State,Creator_ID,Created_Time,Updated_By,Updated_Time,Age
/// </summary>
public interface IBasUserDAO : IDAOBase, IBasUserFactory, IBasUserParamFactory
{
……
(3)SampleModel.cs:
/*
下列代码由 [代码生成器 V1.0] 生成
时间:2007-05-12 17:57:21
*/
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using AppFramework.Data;
/// <summary>
/// 此命名空间下的类或接口由代码生成器生成
/// </summary>
namespace Demo.Model
{
/// <summary>
/// BAS_User 表实体类接口,处理字段包括:
/// ID,Name,En_Name,Password,Dept_ID,Org_ID,Employee_No,Email,State,Creator_ID,Created_Time,Updated_By,Updated_Time,Age
/// </summary>
public interface IBasUser : IInfoBase
{
……
上述代码只列出片段,具体情况可以查看示例程序。
1.5 编写简单的程序
1.5.1 相关类的关系图
有两个基础类,一个是DAOManager,可以获得IXxxDAO接口;另一个是IDBSession,从DBSessionManager.GetSession()获得。IXxxDAO的大部分数据库访问方法都要依赖IDBSession来实现。
1.5.2 引用
引用DLL:
AppFramework.Data.dll
AppFramework.DBAccess.dll
引用命名空间:
using AppFramework.Data;
using AppFramework.DBAccess;
using Xxx.Model;
using Xxx.Access.Interface;
1.5.3 插入一个实体
// 构造用户信息参数类
IBasUserParam user = new BasUserParam();
user.Name.Value = "MyBasName";
user.EnName.Value = "EnMyBasName" ;
user.CreatorID.Value = 0;
user.DeptID.Value = 100;
user.Email.Value = "Email";
user.EmployeeNo.Value = "EmployeeNO" ;
user.OrgID.Value = 0;
user.Password.Value = "Password";
user.State.Value = 0;
user.UpdatedBy.Value = 0;
user.Age.Value = 25;
// 获得dao
IBasUserDAO dao = DAOManager.Default.GetDAO<IBasUserDAO>();
// 获得数据库会话
using (IDBSession session = DBSessionManager.Default.GetSession())
{
dao.Insert(session, user); // 插入数据库
}
// 获得新记录的ID
int id = user.ID.Value;
1.5.4 根据主键获取一个实体
public BasUser Get(string fields, int id)
{
// 获得dao
IBasUserDAO dao = DAOManager.Default.GetDAO<IBasUserDAO>();
// 获得数据库会话
using (IDBSession session = DBSessionManager.Default.GetSession())
{
return dao.Get(session, fields, id);//返回得到的实体,如果未找到记录,返回null
}
}
1.5.5 根据主键更新一个实体
// 构造用户信息参数类
IBasUserParam user = new BasUserParam();
user.ID.Value = 100;// 设置要更新记录的主键值
user.Name.Value = "MyBasName";
user.EnName.Value = "EnMyBasName" ;
user.CreatorID.Value = 0;
user.DeptID.Value = 100;
user.Email.Value = "Email";
user.EmployeeNo.Value = "EmployeeNO" ;
user.OrgID.Value = 0;
user.Password.Value = "Password";
user.State.Value = 0;
user.UpdatedBy.Value = 0;
user.Age.Value = 25;
// 获得dao
IBasUserDAO dao = DAOManager.Default.GetDAO<IBasUserDAO>();
// 获得数据库会话
using (IDBSession session = DBSessionManager.Default.GetSession())
{
return dao.Update(session, user); // 更新记录,返回真实更新的记录数
}
1.5.6 根据主键删除一个实体
public int DeleteUser(int id)
{
// 获得dao
IBasUserDAO dao = DAOManager.Default.GetDAO<IBasUserDAO>();
// 获得数据库会话
using (IDBSession session = DBSessionManager.Default.GetSession())
{
return dao.Delete(session, id); // 根据id删除记录,返回真实删除的记录数
}
}
1.5.7 查询实体集
假设界面上有三个查询条件,如下所示:
点击“查找”按钮后执行查询,代码如下:
QueryFilter filter = new QueryFilter();
filter.AddString(BasUserDef.Name_FieldName, txtUserName.Text, QueryFuzzyType.RightFuzzy);
filter.AddDateRangeBegin(BasUserDef.UpdatedTime_FieldName, beginDate.Text);
filter.AddDateRangeEnd(BasUserDef.UpdatedTime_FieldName, endDate.Text);
string fields = “ID, Name, Updated_Time”;
string orderBy = “ID asc”;
// 获得dao
IBasUserDAO dao = DAOManager.Default.GetDAO<IBasUserDAO>();
// 获得数据库会话
using (IDBSession session = DBSessionManager.Default.GetSession())
{
ObjectTable<BasUser> users = dao.SelectList(session, fields, filter, orderBy); // 执行查询,获得结果集
}
其中关键的一个类是:QueryFilter,通过它可以拼装出几乎所有可能的查询逻辑。具体可以参考“精通”篇相关内容。
1.5.8 事务处理
有关数据库事务的IDBSession方法如下所示:
// 摘要:
// 开始新事务,重复执行此操作只会增加嵌套级别但不出错。
void BeginTran();
//
// 摘要:
// 以 level 事务级别开始新事务,重复执行此操作只会增加嵌套级别但不出错。
void BeginTran(IsolationLevel level);
//
// 摘要:
// 提交事务,若有事务嵌套,则只在最外层才提交。如果事务尚未打开则抛出 TransactionNotBeginException。
void CommitTran();
//
// 摘要:
// 如果已启动事务,无论嵌套多少级别回滚整个事务,重复调用也不出错。
void RollbackTran();
例子:
// 获得数据库会话
using (IDBSession session = DBSessionManager.Default.GetSession())
{
try
{
session.BeginTran();
dao.Update(session, user);
dao.Delete(session, 100);
……
session.ComminTran();
}
catch (Exception e)
{
session.RollbackTran();
}
}