cad.net 事务处理
记录IFox的事务错误
IFox利用了自己的事务栈来管理的事务,事务栈是记录在静态字段上,
使得事务的生命无限延长,通过using来释放并自动提交.
子函数是通过DBTrans.Top获取栈顶事务,就不用传参了.
但是IFox前期版本的栈空
就直接创建了,
这样导致了命令结束了也不会释放事务,
令到ctrl+z直接回滚到上个事务,也可能导致虽显但无法选择图元,
所以我在Top上面加了一个报错,
栈空
的时候必须要用户先创建事务:using DBTrans tr = new();
引起undo撤回错误的代码
报错0x01
!dbenti.cpp@3310:eWasErased错误
引起错误的代码:
好的代码:
(这段代码的两个事务是单独的事务,没有被另一个大的事务包裹,是好的代码)
原理
两段代码仅仅是事务提交的方式不同,
这两段代码顺序执行都不会引起任何错误,但是 ctrl+z 回滚操作时候右边却会引起
引起错误的原因是:
ctrl+z 撤回操作会根据事务进行回滚,如果一起回滚,
那么回滚的时候会先按照处理的图元回滚图层,
但是此时因为图层的删除还没有恢复,缺少图层的记录,
所以将他们分层两个事务,回滚操作就会先恢复图层,再进行回滚图元的修改.
报错0x02
0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。
操作时候仍然是顺序执行没有问题,但是使用了ctrl+z就出现,此报错原理不明,大家可以尽情猜测.
引起错误的代码:生成四叉树所有节点边界
生成十万个数量的矩形,因为多段线的数据量大,因此引起了这个错误.
db.Action(tr => {
foreach (var item in nodeRects)
{
var pts = item.ToPoints();
var rec = EntityAdd.AddPolyLineToEntity(pts.ToPoint2d());
rec.ColorIndex = 250;
tr.AddEntityToMsPs(db, rec);
}
});
好的代码
解决方案依然是将事务拆分到循环体内部
foreach (var item in nodeRects)//Count = 97341 当数量接近这个量级
{
db.Action(tr => {
var pts = item.ToPoints();
var rec = EntityAdd.AddPolyLineToEntity(pts.ToPoint2d());
rec.ColorIndex = 250;
tr.AddEntityToMsPs(db, rec);
});
}
验证事务Abort
public class CmdTest_Tr
{
static ObjectId? objectId;
static Handle? handle;
[CommandMethod("CmdTest_TryAbort1", CommandFlags.DocExclusiveLock)]
public void CmdTest_TryAbort1()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
ed.WriteMessage("\n测试cad事务1");
using (var tr = db.TransactionManager.StartTransaction())
{
var line = new Line(Point3d.Origin, new Point3d(1000, 1000, 0));
objectId = tr.AddEntityToMsPs(db, line);//https://www.cnblogs.com/JJBox/p/14300098.html
handle = line.Handle;
//为了测试自动释放不是等于下句....经过测试,写不写都一样的!
//tr.Abort();//撤销
}
}
[CommandMethod("CmdTest_TryAbort2", CommandFlags.DocExclusiveLock)]
public void CmdTest_TryAbort2()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
ed.WriteMessage("\n测试cad事务2");
if (handle == null)
ed.WriteMessage("\n句柄 空啊");// (handent "1DE") 获取句柄图元名,如果是IsErased那么这里返回nil
var id2 = handle.Value.TryGetObjectId(db);
if (id2 == ObjectId.Null)
ed.WriteMessage("\nid2 空啊");
else
{
ed.WriteMessage($"\nid2 不空啊,id.IsErased:{objectId.Value.IsErased}");
using (var tr = db.TransactionManager.StartTransaction())
{
var ent = objectId.Value.ToEntity(tr);
if (ent == null)
ed.WriteMessage("图元是空的");
else
ed.WriteMessage("图元是不空的");
}
}
if (objectId == null)
ed.WriteMessage("\nid 空啊");
else
{
ed.WriteMessage($"\nid 不空啊,id.IsErased:{objectId.Value.IsErased}");
using (var tr = db.TransactionManager.StartTransaction())
{
var ent = objectId.Value.ToEntity(tr);
if (ent == null)
ed.WriteMessage("图元是空的");
else
ed.WriteMessage("图元是不空的");
}
}
}
}