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框架所有实体类的基类,它实现了"充血实体类"模型,具有以下特性:

  1. 动态属性存储:属性值存储在内部字典中
  2. 状态追踪:记录哪些属性被修改过
  3. 元数据映射:动态定义表名、主键、字段映射
  4. 索引器访问:支持通过字符串名称访问属性

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查询语言,设计目标是:

  1. 接近SQL语法:让有SQL经验的开发者零学习成本
  2. 类型安全:编译期检查,避免运行时错误
  3. 链式调用:流畅的API设计
  4. 多表支持:支持复杂的关联查询

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并执行的核心类,主要职责:

  1. 执行OQL查询
  2. 将结果映射到实体对象
  3. 执行实体的增删改操作

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框架的核心架构和主要组件:

  1. AdoHelper:抽象数据访问层,屏蔽数据库差异
  2. EntityBase:充血实体类基类,支持动态映射和状态追踪
  3. OQL/GOQL:类SQL的ORM查询语言,链式调用
  4. EntityQuery:执行查询和增删改操作
  5. EntityContainer:处理多表查询的结果映射
  6. DbContext:提供Code First和事务支持

理解这些核心组件的设计思想和协作方式,是掌握SOD框架的基础。在后续章节中,我们将深入学习每个组件的具体使用方法。


下一章预告:第三章将介绍如何快速入门SOD框架,包括环境配置、项目搭建和第一个示例程序。

posted @ 2025-11-29 13:40  我才是银古  阅读(4)  评论(0)    收藏  举报