惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
A
Arctic Wolf
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Proofpoint News Feed
S
Schneier on Security
The Cloudflare Blog
Security Latest
Security Latest
S
SegmentFault 最新的问题
L
Lohrmann on Cybersecurity
T
Tailwind CSS Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
Latest news
Latest news
月光博客
月光博客
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
F
Fortinet All Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
美团技术团队
博客园 - 叶小钗
H
Help Net Security
博客园 - 【当耐特】
云风的 BLOG
云风的 BLOG
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
A
About on SuperTechFans
WordPress大学
WordPress大学
Cloudbric
Cloudbric
IT之家
IT之家
Last Week in AI
Last Week in AI
S
Securelist
Google Online Security Blog
Google Online Security Blog
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
L
LINUX DO - 最新话题
Know Your Adversary
Know Your Adversary
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
G
Google Developers Blog
C
CERT Recently Published Vulnerability Notes
宝玉的分享
宝玉的分享
博客园 - Franky
TaoSecurity Blog
TaoSecurity Blog
P
Proofpoint News Feed
大猫的无限游戏
大猫的无限游戏
Jina AI
Jina AI
L
LINUX DO - 热门话题
N
News and Events Feed by Topic
B
Blog

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第二章:DBTrans事务管理核心教程
我才是银古 · 2026-06-20 · via 博客园 - 我才是银古

第二章:DBTrans事务管理核心教程

2.1 事务机制基础

2.1.1 什么是事务

在AutoCAD二次开发中,事务(Transaction)是一个至关重要的概念。事务提供了一种安全可靠的方式来修改CAD数据库中的对象。可以将事务理解为一个"工作单元",在这个工作单元中执行的所有操作要么全部成功,要么全部失败。

事务的核心特性包括:

原子性(Atomicity)

事务中的所有操作被视为一个不可分割的整体。如果事务中的任何一个操作失败,整个事务都会回滚,数据库恢复到事务开始之前的状态。这确保了数据的完整性。

一致性(Consistency)

事务完成后,数据库必须处于一致的状态。在CAD中,这意味着所有的图元引用、符号表记录等都必须是有效和一致的。

隔离性(Isolation)

在事务进行中,其他事务不能看到当前事务的中间状态。这在多线程或多进程环境下尤其重要。

持久性(Durability)

一旦事务提交,其结果就是永久性的,即使系统发生故障也不会丢失。

2.1.2 CAD原生事务的使用

在使用IFoxCAD之前,让我们先回顾一下CAD原生API中事务的使用方式:

// CAD原生事务使用方式
public void NativeTransactionExample()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // 打开块表
        BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
        
        // 打开模型空间
        BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], 
            OpenMode.ForWrite) as BlockTableRecord;
        
        // 创建直线
        Line line = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0));
        
        // 添加到模型空间
        btr.AppendEntity(line);
        tr.AddNewlyCreatedDBObject(line, true);
        
        // 提交事务
        tr.Commit();
    }
}

这种方式的问题在于:

  1. 代码冗长:每次操作都需要获取Document、Database、启动事务等一系列步骤
  2. 重复性高:打开块表、模型空间的代码在每个方法中都需要重复
  3. 容易出错:忘记Commit、忘记AddNewlyCreatedDBObject等常见错误
  4. 可读性差:业务逻辑被大量的样板代码所淹没

2.1.3 DBTrans的设计目标

DBTrans类的设计目标就是解决上述问题,它提供了:

  1. 简洁的API:一行代码创建事务,自动管理提交和释放
  2. 封装常用对象:Document、Database、Editor、符号表等直接作为属性访问
  3. 减少错误:默认自动提交,自动添加新创建的对象
  4. 提高可读性:让开发者专注于业务逻辑,而不是样板代码

使用DBTrans后,上面的代码可以简化为:

// 使用DBTrans的方式
public void DBTransExample()
{
    using var tr = new DBTrans();
    var line = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0));
    tr.CurrentSpace.AddEntity(line);
}

代码量大大减少,逻辑更加清晰。

2.2 DBTrans类详解

2.2.1 DBTrans的类结构

DBTrans是IFoxCAD中最核心的类,让我们来详细了解它的结构:

public sealed class DBTrans : IDisposable
{
    // 静态属性
    public static DBTrans Top { get; }          // 获取顶层事务
    
    // 实例属性
    public Document? Document { get; }          // 文档对象
    public Editor? Editor { get; }              // 编辑器对象
    public Transaction Transaction { get; }     // 底层事务对象
    public Database Database { get; }           // 数据库对象
    
    // 符号表属性
    public SymbolTable<BlockTable, BlockTableRecord> BlockTable { get; }
    public SymbolTable<LayerTable, LayerTableRecord> LayerTable { get; }
    public SymbolTable<LinetypeTable, LinetypeTableRecord> LinetypeTable { get; }
    // ... 其他符号表
    
    // 特殊块表记录
    public BlockTableRecord CurrentSpace { get; }  // 当前空间
    public BlockTableRecord ModelSpace { get; }    // 模型空间
    public BlockTableRecord PaperSpace { get; }    // 图纸空间
    
    // 字典属性
    public DBDictionary NamedObjectsDict { get; }  // 命名对象字典
    public DBDictionary GroupDict { get; }         // 组字典
    public DBDictionary LayoutDict { get; }        // 布局字典
    // ... 其他字典
    
    // 方法
    public DBObject GetObject(ObjectId id, OpenMode openMode = OpenMode.ForRead, ...);
    public T? GetObject<T>(ObjectId id, OpenMode openMode = OpenMode.ForRead, ...) where T : DBObject;
    public void Task(Action action, bool handlingDBTextDeviation = true);
    public void Abort();
    public void Commit();
    public void Dispose();
}

2.2.2 构造函数详解

DBTrans提供了三个构造函数,分别用于不同的使用场景:

构造函数1:基于文档的构造

public DBTrans(Document? doc = null, bool commit = true, bool docLock = false)

参数说明:

  • doc:要操作的文档对象,默认为当前活动文档
  • commit:事务结束时是否自动提交,默认为true
  • docLock:是否锁定文档,默认为false

使用示例:

// 操作当前文档,自动提交
using var tr1 = new DBTrans();

// 操作当前文档,手动控制提交
using var tr2 = new DBTrans(commit: false);
// ... 操作
tr2.Commit(); // 手动提交

// 操作指定文档,锁定文档
var targetDoc = Application.DocumentManager.GetDocument(someDatabase);
using var tr3 = new DBTrans(targetDoc, true, true);

构造函数2:基于数据库的构造

public DBTrans(Database database, bool commit = true)

参数说明:

  • database:要操作的数据库对象
  • commit:事务结束时是否自动提交,默认为true

使用示例:

// 操作外部数据库
Database extDb = new Database(false, true);
extDb.ReadDwgFile("C:\\drawing.dwg", FileOpenMode.OpenForReadAndWriteNoShare, true, null);

using var tr = new DBTrans(extDb);
// 操作数据库...

构造函数3:基于文件的构造

public DBTrans(string fileName, bool commit = true,
    FileOpenMode fileOpenMode = FileOpenMode.OpenForReadAndWriteNoShare,
    string? password = null, bool activeOpen = false)

参数说明:

  • fileName:要打开的DWG/DXF文件路径
  • commit:事务结束时是否自动提交,默认为true
  • fileOpenMode:文件打开模式
  • password:文件密码(如果有的话)
  • activeOpen:是否前台打开,默认为后台打开

使用示例:

// 后台打开文件进行处理
using var tr1 = new DBTrans("C:\\drawing.dwg");
// 处理文件...

// 前台打开文件(需要CommandFlags.Session)
using var tr2 = new DBTrans("C:\\drawing.dwg", true, 
    FileOpenMode.OpenForReadAndWriteNoShare, null, true);

2.2.3 事务栈机制

DBTrans内部维护了一个静态的事务栈,用于管理嵌套事务:

private static readonly Stack<DBTrans> _dBTrans = new();

事务栈的工作原理

当创建一个新的DBTrans实例时,它会被压入事务栈:

_dBTrans.Push(this);

当DBTrans被释放时,它会从事务栈中弹出:

_dBTrans.Pop();

获取顶层事务

可以通过静态属性Top获取当前的顶层事务:

// 在某个方法中,假设已经有事务存在
public void SomeMethod()
{
    var topTrans = DBTrans.Top;
    // 使用顶层事务...
}

注意:如果事务栈为空,访问Top属性会抛出异常。在调用之前应确保已创建事务。

获取指定数据库的顶层事务

public static DBTrans GetTop(Database? database = null)

这个方法可以获取指定数据库的顶层DBTrans事务,如果不指定数据库则使用当前工作数据库。

2.2.4 获取对象方法

DBTrans提供了两个获取对象的方法:

非泛型版本

public DBObject GetObject(ObjectId id, 
    OpenMode openMode = OpenMode.ForRead,
    bool openErased = false, 
    bool openLockedLayer = false)

泛型版本

public T? GetObject<T>(ObjectId id, 
    OpenMode openMode = OpenMode.ForRead,
    bool openErased = false, 
    bool openLockedLayer = false) where T : DBObject

参数说明:

  • id:对象的ObjectId
  • openMode:打开模式,ForRead(只读)、ForWrite(读写)、ForNotify(通知)
  • openErased:是否打开已删除的对象
  • openLockedLayer:是否打开锁定图层上的对象

使用示例:

using var tr = new DBTrans();

// 获取一个直线对象(只读)
var line = tr.GetObject<Line>(lineId);

// 获取一个直线对象(读写)
var lineForEdit = tr.GetObject<Line>(lineId, OpenMode.ForWrite);
lineForEdit.Color = Color.FromColorIndex(ColorMethod.ByAci, 1);

// 获取已删除的对象
var erasedEnt = tr.GetObject<Entity>(erasedId, OpenMode.ForRead, true);

2.3 符号表操作

2.3.1 九大符号表概述

AutoCAD数据库中包含九个符号表,DBTrans为每个符号表都提供了对应的属性:

符号表 DBTrans属性 说明
BlockTable BlockTable 块表,存储所有块定义
LayerTable LayerTable 图层表,存储所有图层
LinetypeTable LinetypeTable 线型表,存储所有线型
TextStyleTable TextStyleTable 文字样式表
DimStyleTable DimStyleTable 标注样式表
RegAppTable RegAppTable 注册应用程序表
UcsTable UcsTable 用户坐标系表
ViewTable ViewTable 视图表
ViewportTable ViewportTable 视口表

2.3.2 块表操作

块表是最常用的符号表,它存储了所有的块定义,包括模型空间和图纸空间。

访问模型空间和图纸空间

using var tr = new DBTrans();

// 获取当前空间(模型空间或当前布局的图纸空间)
BlockTableRecord currentSpace = tr.CurrentSpace;

// 获取模型空间
BlockTableRecord modelSpace = tr.ModelSpace;

// 获取图纸空间
BlockTableRecord paperSpace = tr.PaperSpace;

添加图元到空间

using var tr = new DBTrans();

// 创建图元
var line = new Line(Point3d.Origin, new Point3d(100, 100, 0));
var circle = new Circle(new Point3d(50, 50, 0), Vector3d.ZAxis, 25);

// 添加到当前空间
tr.CurrentSpace.AddEntity(line);
tr.CurrentSpace.AddEntity(circle);

// 或者批量添加
tr.CurrentSpace.AddEntity(new Entity[] { line, circle });

创建块定义

using var tr = new DBTrans();

// 创建新的块定义
tr.BlockTable.Add("MyBlock", btr =>
{
    // 在块定义中添加图元
    var line = new Line(Point3d.Origin, new Point3d(10, 10, 0));
    var circle = new Circle(Point3d.Origin, Vector3d.ZAxis, 5);
    
    btr.AppendEntity(line);
    tr.Transaction.AddNewlyCreatedDBObject(line, true);
    btr.AppendEntity(circle);
    tr.Transaction.AddNewlyCreatedDBObject(circle, true);
});

插入块参照

using var tr = new DBTrans();

// 获取块定义的ID
ObjectId blockId = tr.BlockTable["MyBlock"];

if (!blockId.IsNull)
{
    // 创建块参照
    var blockRef = new BlockReference(new Point3d(50, 50, 0), blockId);
    
    // 添加到当前空间
    tr.CurrentSpace.AddEntity(blockRef);
}

2.3.3 图层表操作

创建图层

using var tr = new DBTrans();

// 创建新图层
tr.LayerTable.Add("MyLayer", layer =>
{
    layer.Color = Color.FromColorIndex(ColorMethod.ByAci, 1); // 红色
    layer.LineWeight = LineWeight.LineWeight030;
    layer.Description = "我的图层";
});

检查图层是否存在

using var tr = new DBTrans();

if (tr.LayerTable.Has("MyLayer"))
{
    tr.Editor?.WriteMessage("\n图层已存在!");
}
else
{
    tr.LayerTable.Add("MyLayer");
}

修改图层属性

using var tr = new DBTrans();

tr.LayerTable.Change("MyLayer", layer =>
{
    layer.Color = Color.FromColorIndex(ColorMethod.ByAci, 3); // 绿色
    layer.IsFrozen = false;
    layer.IsOff = false;
    layer.IsLocked = false;
});

删除图层

using var tr = new DBTrans();

// 删除图层(注意:只能删除未使用且非系统图层)
tr.LayerTable.Remove("MyLayer");

2.3.4 其他符号表操作

线型表操作

using var tr = new DBTrans();

// 加载线型(如果不存在)
if (!tr.LinetypeTable.Has("DASHED"))
{
    tr.Database.LoadLineTypeFile("DASHED", "acad.lin");
}

// 使用线型
var line = new Line(Point3d.Origin, new Point3d(100, 0, 0));
line.LinetypeId = tr.LinetypeTable["DASHED"];
tr.CurrentSpace.AddEntity(line);

文字样式表操作

using var tr = new DBTrans();

// 创建文字样式
tr.TextStyleTable.Add("MyTextStyle", style =>
{
    style.FileName = "simhei.ttf"; // 使用黑体
    style.BigFontFileName = "";
    style.TextSize = 2.5;
});

// 使用文字样式创建文字
var text = new DBText();
text.TextString = "Hello IFoxCAD";
text.Position = new Point3d(0, 0, 0);
text.Height = 5;
text.TextStyleId = tr.TextStyleTable["MyTextStyle"];
tr.CurrentSpace.AddEntity(text);

标注样式表操作

using var tr = new DBTrans();

// 创建标注样式
tr.DimStyleTable.Add("MyDimStyle", dimStyle =>
{
    dimStyle.Dimtxt = 2.5;      // 文字高度
    dimStyle.Dimasz = 2.5;      // 箭头大小
    dimStyle.Dimexe = 1.25;     // 尺寸界线超出量
    dimStyle.Dimexo = 0.625;    // 尺寸界线偏移量
});

2.4 字典操作

2.4.1 命名字典概述

除了符号表,AutoCAD数据库还包含多个命名字典用于存储各种非图形数据。DBTrans提供了以下字典属性:

字典 DBTrans属性 说明
命名对象字典 NamedObjectsDict 根字典
组字典 GroupDict 存储组定义
布局字典 LayoutDict 存储布局
材质字典 MaterialDict 存储材质
多重引线样式字典 MLeaderStyleDict 存储多重引线样式
多线样式字典 MLStyleDict 存储多线样式
表格样式字典 TableStyleDict 存储表格样式
视觉样式字典 VisualStyleDict 存储视觉样式
打印设置字典 PlotSettingsDict 存储打印设置
颜色字典 ColorDict 存储命名颜色

2.4.2 组操作

组是将多个图元关联在一起的方式,通过GroupDict可以管理组。

using var tr = new DBTrans();

// 创建一些图元
var line = new Line(Point3d.Origin, new Point3d(100, 0, 0));
var circle = new Circle(new Point3d(50, 0, 0), Vector3d.ZAxis, 10);

tr.CurrentSpace.AddEntity(line);
tr.CurrentSpace.AddEntity(circle);

// 创建组
using (tr.GroupDict.ForWrite())
{
    var group = new Group("我的组", true);
    tr.GroupDict.SetAt("MyGroup", group);
    tr.Transaction.AddNewlyCreatedDBObject(group, true);
    
    // 将图元添加到组
    group.Append(new ObjectId[] { line.ObjectId, circle.ObjectId });
}

2.4.3 自定义字典

可以在命名对象字典下创建自定义字典来存储应用程序数据。

using var tr = new DBTrans();

// 获取或创建自定义字典
DBDictionary myDict;
if (tr.NamedObjectsDict.Contains("MyAppDict"))
{
    myDict = tr.GetObject<DBDictionary>(tr.NamedObjectsDict.GetAt("MyAppDict"));
}
else
{
    using (tr.NamedObjectsDict.ForWrite())
    {
        myDict = new DBDictionary();
        tr.NamedObjectsDict.SetAt("MyAppDict", myDict);
        tr.Transaction.AddNewlyCreatedDBObject(myDict, true);
    }
}

// 在自定义字典中存储数据
using (myDict.ForWrite())
{
    var xrec = new Xrecord();
    xrec.Data = new ResultBuffer(
        new TypedValue((int)DxfCode.Text, "Hello"),
        new TypedValue((int)DxfCode.Int32, 123)
    );
    myDict.SetAt("MyData", xrec);
    tr.Transaction.AddNewlyCreatedDBObject(xrec, true);
}

2.5 前台与后台任务处理

2.5.1 前台后台的区别

在CAD二次开发中,有两种操作模式:

前台操作

前台操作是在当前打开的文档中进行操作。用户可以看到操作的结果,操作受到文档锁的限制。

后台操作

后台操作是在没有打开的文档上进行操作,通常用于批量处理文件。用户看不到操作的过程,但处理速度更快。

2.5.2 Task方法的使用

DBTrans提供了Task方法来统一处理前台和后台任务的差异:

public void Task(Action action, bool handlingDBTextDeviation = true)

参数说明:

  • action:要执行的操作委托
  • handlingDBTextDeviation:是否处理单行文字偏移问题,默认为true

使用示例:

using var tr = new DBTrans("C:\\drawing.dwg");

tr.Task(() =>
{
    // 这里的代码在前台和后台都能正确执行
    // 自动处理单行文字偏移等问题
    
    foreach (var id in tr.ModelSpace)
    {
        var ent = tr.GetObject<Entity>(id);
        if (ent is DBText text)
        {
            // 处理文字...
        }
    }
});

2.5.3 文件批量处理示例

[CommandMethod("BatchProcess", CommandFlags.Session)]
public void BatchProcess()
{
    var files = Directory.GetFiles("C:\\Drawings", "*.dwg");
    
    foreach (var file in files)
    {
        using var tr = new DBTrans(file);
        
        tr.Task(() =>
        {
            // 处理每个文件
            // 例如:统一修改图层颜色
            tr.LayerTable.Change("0", layer =>
            {
                layer.Color = Color.FromColorIndex(ColorMethod.ByAci, 7);
            });
        });
        
        // 保存更改
        tr.Database.SaveAs(file, DwgVersion.Current);
    }
}

2.6 事务的提交与取消

2.6.1 自动提交

默认情况下,DBTrans在Dispose时会自动提交事务:

using var tr = new DBTrans(); // commit参数默认为true

var line = new Line(Point3d.Origin, new Point3d(100, 100, 0));
tr.CurrentSpace.AddEntity(line);

// using块结束时自动提交

2.6.2 手动控制提交

可以通过构造函数参数禁用自动提交,然后手动控制:

using var tr = new DBTrans(commit: false);

try
{
    var line = new Line(Point3d.Origin, new Point3d(100, 100, 0));
    tr.CurrentSpace.AddEntity(line);
    
    // 验证操作
    if (ValidateOperation())
    {
        tr.Commit(); // 手动提交
    }
    else
    {
        tr.Abort(); // 取消事务
    }
}
catch (Exception ex)
{
    tr.Abort(); // 发生异常时取消
    throw;
}

2.6.3 Commit和Abort方法

Commit方法

public void Commit()
{
    _commit = true;
    Dispose();
}

调用Commit会设置提交标志并触发Dispose,事务中的更改会被保存。

Abort方法

public void Abort()
{
    _commit = false;
    Dispose();
}

调用Abort会清除提交标志并触发Dispose,事务中的更改会被回滚。

2.7 最佳实践

2.7.1 事务的作用域

原则1:尽量减少事务的作用域

事务应该尽可能短,只包含必要的数据库操作:

// 好的做法
public void GoodPractice()
{
    // 准备数据(不需要事务)
    var points = CalculatePoints();
    
    // 只在数据库操作时使用事务
    using var tr = new DBTrans();
    foreach (var pt in points)
    {
        var circle = new Circle(pt, Vector3d.ZAxis, 10);
        tr.CurrentSpace.AddEntity(circle);
    }
}

// 不好的做法
public void BadPractice()
{
    using var tr = new DBTrans();
    
    // 复杂的计算在事务中进行(不必要)
    var points = CalculateComplexPoints(); // 耗时操作
    
    foreach (var pt in points)
    {
        var circle = new Circle(pt, Vector3d.ZAxis, 10);
        tr.CurrentSpace.AddEntity(circle);
    }
}

原则2:避免嵌套事务

虽然DBTrans支持嵌套,但应尽量避免:

// 不推荐的嵌套方式
using var tr1 = new DBTrans();
using var tr2 = new DBTrans(); // 嵌套事务

// 推荐的方式:复用同一个事务
using var tr = new DBTrans();
DoOperation1(tr);
DoOperation2(tr);

2.7.2 异常处理

使用try-catch处理异常

[CommandMethod("SafeOperation")]
public void SafeOperation()
{
    try
    {
        using var tr = new DBTrans(commit: false);
        
        // 执行操作
        PerformDatabaseOperations(tr);
        
        // 操作成功则提交
        tr.Commit();
    }
    catch (System.Exception ex)
    {
        // 事务会自动回滚
        Application.ShowAlertDialog($"操作失败:{ex.Message}");
    }
}

2.7.3 文档锁处理

在需要跨文档操作或从外部事件触发操作时,需要锁定文档:

// 使用docLock参数
using var tr = new DBTrans(doc, true, docLock: true);

// 或者手动锁定
using (doc.LockDocument())
using (var tr = new DBTrans(doc))
{
    // 操作...
}

2.7.4 性能优化

批量添加图元

using var tr = new DBTrans();

// 好的做法:使用批量添加
var entities = CreateManyEntities();
tr.CurrentSpace.AddEntity(entities);

// 不推荐:循环中单个添加
foreach (var ent in entities)
{
    tr.CurrentSpace.AddEntity(ent);
}

延迟打开写模式

using var tr = new DBTrans();

// 先读取,判断后再写
var layer = tr.LayerTable.GetRecord("MyLayer");
if (layer != null && layer.Color.ColorIndex != 1)
{
    using (layer.ForWrite()) // 需要修改时才打开写模式
    {
        layer.Color = Color.FromColorIndex(ColorMethod.ByAci, 1);
    }
}

2.8 本章小结

本章我们深入学习了DBTrans事务管理类,这是IFoxCAD最核心的类:

  1. 事务机制基础:理解了事务的概念和CAD原生事务的使用方式
  2. DBTrans类详解:学习了DBTrans的结构、构造函数、事务栈机制和获取对象方法
  3. 符号表操作:掌握了通过DBTrans操作九大符号表的方法
  4. 字典操作:学习了命名字典和自定义字典的使用
  5. 前台后台任务:理解了Task方法的作用和文件批量处理
  6. 事务控制:掌握了事务的提交、取消和手动控制
  7. 最佳实践:学习了事务使用的各种最佳实践

通过DBTrans,我们可以用简洁的代码完成复杂的数据库操作。下一章我们将深入学习SymbolTable类,进一步掌握符号表的高级操作技巧。