cad.net 事务处理

记录IFox的事务错误

IFox利用了自己的事务栈来管理的事务,事务栈是记录在静态字段上,
使得事务的生命无限延长,通过using来释放并自动提交.
子函数是通过DBTrans.Top获取栈顶事务,就不用传参了.

但是IFox前期版本的栈空就直接创建了,
这样导致了命令结束了也不会释放事务,
令到ctrl+z直接回滚到上个事务,也可能导致虽显但无法选择图元,
所以我在Top上面加了一个报错,
栈空的时候必须要用户先创建事务:using DBTrans tr = new();

引起undo撤回错误的代码

报错0x01

!dbenti.cpp@3310:eWasErased错误

引起错误的代码:
img

好的代码:
img
(这段代码的两个事务是单独的事务,没有被另一个大的事务包裹,是好的代码)

原理
两段代码仅仅是事务提交的方式不同,
这两段代码顺序执行都不会引起任何错误,但是 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("图元是不空的");
            }
        }
    }
}

相关阅读

cad.net undo返回操作

posted @ 2019-05-01 01:47  惊惊  阅读(1020)  评论(0编辑  收藏  举报