02-框架架构与核心组件
第二章:框架架构与核心组件
2.1 整体架构设计
2.1.1 分层架构
SOD框架采用经典的分层架构设计,从底层到顶层依次为:
┌───────────────────────────────────────────────────────────────┐
│ 表示层 (Presentation) │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ PWMIS.Windows │ │ PWMIS.Web │ │
│ │ WinForm控件 │ │ WebForm控件 │ │
│ └─────────────────┘ └─────────────────────────────────┘ │
├───────────────────────────────────────────────────────────────┤
│ 业务逻辑层 (Business Logic) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ DbContext │ │
│ │ 数据上下文(可选,用于Code First) │ │
│ └─────────────────────────────────────────────────────┘ │
├───────────────────────────────────────────────────────────────┤
│ 数据映射层 (Data Mapping) │
│ ┌───────────┐ ┌─────────────┐ ┌───────────────────┐ │
│ │ SQL-MAP │ │ ORM │ │ EntityContainer │ │
│ │ DBMapper │ │EntityQuery │ │ 数据容器 │ │
│ └───────────┘ └─────────────┘ └───────────────────┘ │
├───────────────────────────────────────────────────────────────┤
│ 实体层 (Entity Layer) │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ EntityBase ││
│ │ 实体类基类(动态属性、元数据映射、状态追踪) ││
│ └─────────────────────────────────────────────────────────┘│
├───────────────────────────────────────────────────────────────┤
│ 数据访问层 (Data Access Layer) │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ AdoHelper ││
│ │ 抽象数据访问辅助类(数据库无关的API) ││
│ └─────────────────────────────────────────────────────────┘│
├───────────────────────────────────────────────────────────────┤
│ 数据提供程序层 (Data Provider Layer) │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌─────────┐ │
│ │SqlServer│ │ MySQL │ │ Oracle │ │PostgreSQL││ SQLite │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ └─────────┘ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 达梦 │ │人大金仓 │ │ Access │ ...更多数据库... │
│ └────────┘ └────────┘ └────────┘ │
└───────────────────────────────────────────────────────────────┘
2.1.2 核心设计模式
SOD框架运用了多种经典的设计模式:
抽象工厂模式 (Abstract Factory)
用于创建不同数据库的数据访问提供程序:
// 根据配置自动创建对应数据库的AdoHelper
AdoHelper db = MyDB.GetDBHelper(); // 自动识别数据库类型
单例模式 (Singleton)
EntityQuery采用单例模式,避免重复创建:
// 获取单例实例
var eq = EntityQuery<UserEntity>.Instance;
建造者模式 (Builder)
OQL使用链式调用的建造者模式:
var oql = OQL.From(user)
.Select(user.ID, user.Name) // 构建SELECT
.Where(user.Status) // 构建WHERE
.OrderBy(user.ID) // 构建ORDER BY
.END; // 完成构建
策略模式 (Strategy)
不同数据库的SQL生成策略:
// 框架内部根据数据库类型选择不同的分页策略
// SqlServer: ROW_NUMBER()
// MySQL: LIMIT
// Oracle: ROWNUM
oql.Limit(10, 1); // 自动适配不同数据库
2.2 AdoHelper:数据访问核心
2.2.1 AdoHelper的设计理念
AdoHelper是SOD框架的数据访问核心,它是一个抽象类,提供了与具体数据库无关的数据访问API。这种设计使得上层代码不需要关心底层使用的是什么数据库。
public abstract class AdoHelper
{
// 执行非查询SQL
public abstract int ExecuteNonQuery(string sql, CommandType cmdType, IDataParameter[] paras);
// 执行查询返回DataSet
public abstract DataSet ExecuteDataSet(string sql, CommandType cmdType, IDataParameter[] paras);
// 执行查询返回DataReader
public abstract IDataReader ExecuteReader(string sql, CommandType cmdType, IDataParameter[] paras);
// 执行查询返回标量值
public abstract object ExecuteScalar(string sql, CommandType cmdType, IDataParameter[] paras);
// 获取数据库参数对象
public abstract IDataParameter GetParameter(string name, object value);
// ... 更多抽象方法
}
2.2.2 创建AdoHelper实例
SOD框架提供了多种方式创建AdoHelper实例:
// 方式1:根据连接名称创建(推荐)
AdoHelper db = MyDB.GetDBHelperByConnectionName("local");
// 方式2:使用最后一个连接配置
AdoHelper db = MyDB.GetDBHelper();
// 方式3:直接实例化具体的数据库提供程序
AdoHelper db = new SqlServer();
db.ConnectionString = "Data Source=.;Initial Catalog=MyDB;...";
// 方式4:根据配置字符串动态创建
AdoHelper db = AdoHelper.CreateHelper("local");
2.2.3 核心方法详解
ExecuteNonQuery - 执行非查询操作
// 执行INSERT/UPDATE/DELETE等操作
string sql = "UPDATE User SET Status=@Status WHERE ID=@ID";
IDataParameter[] paras = new IDataParameter[] {
db.GetParameter("@Status", 1),
db.GetParameter("@ID", 100)
};
int affectedRows = db.ExecuteNonQuery(sql, CommandType.Text, paras);
ExecuteDataSet - 返回数据集
// 查询返回DataSet
string sql = "SELECT * FROM User WHERE DeptId=@DeptId";
var paras = new[] { db.GetParameter("@DeptId", 1) };
DataSet ds = db.ExecuteDataSet(sql, CommandType.Text, paras);
DataTable dt = ds.Tables[0];
ExecuteScalar - 返回单个值
// 查询记录数
string sql = "SELECT COUNT(*) FROM User WHERE Status=1";
int count = Convert.ToInt32(db.ExecuteScalar(sql));
ExecuteMapper - 微型ORM映射
// 直接映射到对象列表
string sql = "SELECT ID,Name,Status FROM User WHERE DeptId={0}";
var users = db.ExecuteMapper(sql, 1)
.MapToList(reader => new User {
ID = reader.GetInt32(0),
Name = reader.GetString(1),
Status = reader.GetInt32(2)
});
2.2.4 事务支持
// 简单事务
db.BeginTransaction();
try
{
db.ExecuteNonQuery("INSERT INTO ...");
db.ExecuteNonQuery("UPDATE ...");
db.Commit();
}
catch
{
db.Rollback();
throw;
}
// 使用Session管理连接(推荐)
db.OpenSession();
try
{
db.BeginTransaction();
// 执行多个操作...
db.Commit();
}
finally
{
db.CloseSession();
}
2.3 EntityBase:实体类基类
2.3.1 设计思想
EntityBase是SOD框架所有实体类的基类,它实现了"充血实体类"模型,具有以下特性:
- 动态属性存储:属性值存储在内部字典中
- 状态追踪:记录哪些属性被修改过
- 元数据映射:动态定义表名、主键、字段映射
- 索引器访问:支持通过字符串名称访问属性
2.3.2 核心成员
public abstract class EntityBase
{
// 表名
public string TableName { get; set; }
// 标识字段(自增字段)
public string IdentityName { get; set; }
// 主键集合
public List<string> PrimaryKeys { get; }
// 属性名集合
public string[] PropertyNames { get; }
// 获取属性值
protected T getProperty<T>(string propertyName);
// 设置属性值
protected void setProperty(string propertyName, object value, int length = 0);
// 索引器访问
public object this[string propertyName] { get; set; }
// 设置外键关系
protected void SetForeignKey<T>(string foreignKeyName);
// 获取表名
public string GetTableName();
// 判断属性是否被修改
public bool PropertyChangedList(string propertyName);
}
2.3.3 实体类定义示例
public class UserEntity : EntityBase
{
public UserEntity()
{
// 设置映射的表名
TableName = "TbUser";
// 设置自增标识字段
IdentityName = "ID";
// 设置主键
PrimaryKeys.Add("ID");
}
// 自增主键
public int ID
{
get { return getProperty<int>("ID"); }
set { setProperty("ID", value); }
}
// 普通字段,指定长度为50
public string Name
{
get { return getProperty<string>("Name"); }
set { setProperty("Name", value, 50); }
}
// 可空类型字段
public DateTime? Birthday
{
get { return getProperty<DateTime?>("Birthday"); }
set { setProperty("Birthday", value); }
}
}
2.3.4 动态特性的应用
运行时修改表名(分表)
UserEntity user = new UserEntity();
// 根据用户ID进行分表
int tableIndex = userId % 10;
user.TableName = $"TbUser_{tableIndex}";
var result = OQL.From(user).Select().Where(user.ID).END.ToObject();
// 实际查询的是 TbUser_0, TbUser_1 等表
运行时修改主键
UserEntity user = new UserEntity();
user.PrimaryKeys.Clear();
user.PrimaryKeys.Add("LoginName"); // 用登录名作为主键进行删除
user.LoginName = "zhangsan";
EntityQuery<UserEntity>.Instance.Delete(user);
// 生成: DELETE FROM TbUser WHERE LoginName=@LoginName
2.3.5 索引器的妙用
UserEntity user = new UserEntity();
// 使用索引器设置值
user["Name"] = "张三";
user["Status"] = 1;
// 使用索引器获取值
object name = user["Name"];
// 动态排序场景
string sortField = "CreateTime"; // 来自前端参数
var oql = OQL.From(user)
.Select()
.OrderBy(user[sortField], "desc") // 动态排序
.END;
2.4 OQL:ORM查询语言
2.4.1 OQL的设计理念
OQL(ORM Query Language)是SOD框架独创的ORM查询语言,设计目标是:
- 接近SQL语法:让有SQL经验的开发者零学习成本
- 类型安全:编译期检查,避免运行时错误
- 链式调用:流畅的API设计
- 多表支持:支持复杂的关联查询
2.4.2 OQL vs SQL
-- SQL
SELECT ID, Name, Status
FROM User
WHERE DeptId = 1 AND Status = 1
ORDER BY CreateTime DESC
LIMIT 10 OFFSET 0
// OQL
UserEntity user = new UserEntity();
user.DeptId = 1;
user.Status = 1;
var oql = OQL.From(user)
.Select(user.ID, user.Name, user.Status)
.Where(user.DeptId, user.Status)
.OrderBy(order => order.Desc(user.CreateTime))
.END;
oql.Limit(10, 1);
2.4.3 OQL的语法结构
OQL.From(entity) // 指定查询的实体对象
[.InnerJoin/LeftJoin] // 可选:关联查询
.Select(...) // SELECT子句
.Where(...) // WHERE子句
[.GroupBy(...)] // 可选:分组
[.Having(...)] // 可选:分组条件
.OrderBy(...) // 排序
.END // 结束构建
// 分页(在END之后调用)
oql.Limit(pageSize, pageIndex);
2.4.4 OQL vs GOQL
GOQL(Generic OQL)是OQL的泛型简化版本,适用于单表查询:
// OQL方式 - 需要先创建实体对象
UserEntity user = new UserEntity();
user.Name = "张三";
var oql = OQL.From(user).Select().Where(user.Name).END;
// GOQL方式 - 无需创建实体对象,使用Lambda表达式
var goql = OQL.FromObject<UserEntity>()
.Select()
.Where((cmp, u) => cmp.Property(u.Name) == "张三")
.END;
// 执行查询
var users = goql.ToList();
2.5 EntityQuery:实体查询执行器
2.5.1 职责与功能
EntityQuery是将OQL翻译为SQL并执行的核心类,主要职责:
- 执行OQL查询
- 将结果映射到实体对象
- 执行实体的增删改操作
2.5.2 核心方法
// 查询单个对象
UserEntity user = EntityQuery<UserEntity>.QueryObject(oql, db);
// 查询对象列表
List<UserEntity> users = EntityQuery<UserEntity>.QueryList(oql, db);
// 查询带子实体的对象列表
List<OrderEntity> orders = EntityQuery<OrderEntity>.QueryListWithChild(oql, db);
// 执行OQL(用于UPDATE/DELETE)
int affected = EntityQuery<UserEntity>.ExecuteOql(oql, db);
// 插入实体
EntityQuery<UserEntity>.Instance.Insert(user, db);
// 更新实体
EntityQuery<UserEntity>.Instance.Update(user, db);
// 删除实体
EntityQuery<UserEntity>.Instance.Delete(user, db);
2.5.3 批量操作
// 批量插入
List<UserEntity> users = new List<UserEntity>() { ... };
foreach(var user in users)
{
EntityQuery<UserEntity>.Instance.Insert(user, db);
}
// 批量更新(使用OQL)
UserEntity user = new UserEntity();
user.Status = 0; // 设置要更新的值
var oql = OQL.From(user)
.Update(user.Status)
.Where(cmp => cmp.Property(user.DeptId) == 10)
.END;
int affected = EntityQuery<UserEntity>.ExecuteOql(oql, db);
// 生成: UPDATE TbUser SET Status=0 WHERE DeptId=10
2.6 EntityContainer:数据容器
2.6.1 设计目的
EntityContainer用于处理多表关联查询的结果映射,当查询涉及多个表时,可以将结果映射到不同的类型。
2.6.2 使用场景
// 多表关联查询
OrderEntity order = new OrderEntity();
UserEntity user = new UserEntity();
var oql = OQL.From(order)
.InnerJoin(user).On(order.UserId, user.ID)
.Select()
.Where(cmp => cmp.Property(order.Status) == 1)
.END;
// 使用EntityContainer映射结果
EntityContainer ec = new EntityContainer(oql, db);
// 映射到匿名类型
var list = ec.MapToList(() => new {
OrderId = order.ID,
OrderName = order.Name,
UserName = user.Name,
UserPhone = user.Phone
});
// 映射到指定类型
var orderList = ec.MapToList<OrderEntity>();
var userList = ec.MapToList<UserEntity>();
2.6.3 复杂映射示例
// 查询订单及用户信息,进行计算和格式化
var list = ec.MapToList(() => new OrderViewModel {
OrderId = order.ID,
OrderNo = order.OrderNo,
TotalPrice = order.Price.ToString("C"), // 货币格式
UserName = user.Name,
UserAge = DateTime.Now.Year - user.Birthday.Year, // 计算年龄
StatusText = order.Status == 1 ? "已完成" : "进行中"
});
2.7 DbContext:数据上下文
2.7.1 Code First支持
DbContext提供Code First开发模式支持,可以根据实体类自动创建数据表:
public class MyDbContext : DbContext
{
public MyDbContext() : base("local") // 连接配置名
{
}
protected override bool CheckAllTableExists()
{
// 检查并创建表
CheckTableExists<UserEntity>();
CheckTableExists<OrderEntity>();
// 创建表后初始化(如创建索引)
InitializeTable<OrderEntity>("CREATE INDEX idx_userId ON {0}(UserId)");
return true;
}
}
2.7.2 CRUD操作
var context = new MyDbContext();
// 添加
context.Add(user);
// 批量添加
context.AddList(users);
// 更新
context.Update(user);
// 删除
context.Remove(user);
// 事务
bool success = context.Transaction(ctx => {
ctx.Add(order);
ctx.AddList(orderItems);
}, out string errorMessage);
2.8 组件协作流程
2.8.1 查询流程
用户代码 → OQL构建器 → OQL对象
↓
EntityQuery.QueryList()
↓
SQL生成(根据AdoHelper类型)
↓
AdoHelper.ExecuteReader()
↓
结果映射(反射或表达式树)
↓
返回实体对象列表
2.8.2 插入流程
用户代码 → 实体对象
↓
EntityQuery.Insert()
↓
生成INSERT SQL(排除未修改字段)
↓
AdoHelper.ExecuteNonQuery()
↓
获取自增ID(如果有)
↓
回填实体对象的标识字段
2.8.3 更新流程
用户代码 → 修改实体属性
↓
EntityQuery.Update()
↓
生成UPDATE SQL(只更新修改过的字段)
↓
AdoHelper.ExecuteNonQuery()
↓
返回受影响行数
2.9 本章小结
本章详细介绍了SOD框架的核心架构和主要组件:
- AdoHelper:抽象数据访问层,屏蔽数据库差异
- EntityBase:充血实体类基类,支持动态映射和状态追踪
- OQL/GOQL:类SQL的ORM查询语言,链式调用
- EntityQuery:执行查询和增删改操作
- EntityContainer:处理多表查询的结果映射
- DbContext:提供Code First和事务支持
理解这些核心组件的设计思想和协作方式,是掌握SOD框架的基础。在后续章节中,我们将深入学习每个组件的具体使用方法。
下一章预告:第三章将介绍如何快速入门SOD框架,包括环境配置、项目搭建和第一个示例程序。

浙公网安备 33010602011771号