15. Revit API: Transaction(事务)与 Failures(故障处理)

前言

UI讲完,回到DB这块儿。在Document那篇,提到增删改查操作都是在Document上,是对Documet进行操作。
看到“增删改查”这四个,想到什么了没有?
数据库(DB)嘛~话说那本经典的红皮数据库的书叫啥来着?算了算了,数据库也忘得差不多了😶。
回到Revit API,“删”我们在Document那篇就讲过了,用Delete方法就行了。“查”就是过滤器,上篇的选择器也能沾一点儿边。还有“增”和“改”这俩个。
数据库中的“增”,是增加新的数据,对应Revit就是增加新模型、创建新实例,我们将会在涉及到Creation时讲。
数据库中的“改”,是对原有数据进行修改。在Revit中,就比较宽泛了,或许应该称之为“变化”,变化了就是改了。这块儿,体现在事件上,后面专门些一篇关于Events的,再提改这部分。
回到本篇。
在数据库中,有事务的概念,Revit 的这个功能上差不多,但没有那么细致。
这篇,涉及的主要类如下图
在这里插入图片描述


一、Transaction(事务)

其实在第一篇Namespace与Attributes中,就提到事务,只是那会儿没有展开。

  [Transaction(TransactionMode.Manual)]  // 开启事务

现在我们开始讲事务。
Revit的事务是做什么的,当我们要进行的操作会改变Document时,就必须在事务中进行,比如:删除元素,调整元素参数,设置元素显影。
Revit中,事务类有3个,分别是:

  • Transaction:事务
  • TransactionGroup:事务组,可以在事务组里创建新的事务
  • SubTransaction:子事务,必须在事务中,子事务可嵌套

1.1. 三种事务的对比

来看看方法对比

TransactionTransactionGroupSubTransaction
Start(..) x2Start(..) x2Start
Commit(..) x2Assimilate
Commite
Commite
RollBack(..) x2RollbackRollback
DisposeDisposeDispose
Get/SetNameGet/SetName
GetStatus
HasStarted
HasEnded
GetStatus
HasStarted
HasEnded
GetStatus
HasStarted
HasEnded
GetFailureHandlingOptions
SetFailureHandlingOptions
差异:事务名称,故障处理差异:事务组名称,打包提交

瞧,就这么丢丢的差异。

事务:可以在事务上添加故障处理程序。
事务组:可以将多个事务组织成一个事务提交(Assimilate),也可一次性提交(Commite)。
子事务:只能在事务中开启,本身可嵌套。没自己的名称。

1.2. 事务组的2种提交方式

事务组两种提交方式的区别。

在这里插入图片描述

对于下面的代码,将事务改为子事务,事务组改为事务,也是可以的。

public void TestTransactionGroup(UIDocument uIDoc)
{
    View view = uIDoc.ActiveView;

    List<ElementId> wallIds = this.GetElementByCategory(uIDoc.Document, BuiltInCategory.OST_Walls);
    wallIds = wallIds.Take(5).ToList();  // 取5个

    // 事务组
    using (TransactionGroup transactionGroup = new TransactionGroup(uIDoc.Document, "TransactionGroup-隐藏-所有墙体"))
    {
        transactionGroup.Start();

        foreach (ElementId wallId in wallIds)
        {
            // 事务
            using (Transaction transaction = new Transaction(uIDoc.Document, $"Transaction-隐藏-{wallId.IntegerValue}"))
            {
                transaction.Start();

                view.HideElements([wallId]);

                transaction.Commit();
            }
        }

        //transactionGroup.Assimilate();  // 将5个事务打包成一个,提交
        transactionGroup.Commit();  // 5个事务一次性提交,还是5个
    }
}

1.3. 子事务

上面说了,子事务必须要在事务中才能创建,不然就会报错。
在这里插入图片描述

子事务一般在什么时候使用呢?一般是作为独立的小的操作步骤,插入到有具体业务的事务的逻辑中。
但有时我们需要进行一些操作了,却不能确定当前是否在事务中呢?
这个问题是有意义的,因为事务是不可嵌套的,我们无法在一个事务中开启另一个事务,又无法在非事务中创建子事务,所以需要根据当前的状态,来选择性处理。
还记Document的IsModified属性吗,True表示文档正处于修改中,也就是已开启了事务。

使用document.IsModified来判断当前事务环境。
根据环境不同,来决定是创建 事务 还是 子事务

二、Failures(故障处理)

事务执行失败了怎么办?我们可以回滚Rollback
不想回滚呢?那就try-catch处理问题吧。
但这里的说的故障处理可不是程序上的错误,而是Revit发出的问题。
Revit里有许多的约束,当文档变化时,就会进行校验,判断是否允许更改,常见的问题有“不满足约束”“无法剪切图元”等,在遇到这些问题时,Revit会有弹框,并暂定程序的执行,待用户选择操作后才会继续处理。
这种由Revit发出的警告或错误,是无法通过try-catch处理的,得提供专门的故障处理方式,即实现IFailuresPreprocessor接口,并将其提供给事务。

2.1. IFailuresPreprocessor

IFailuresPreprocessor需要实现一个方法,也只有这个方法。

public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)

方法有个参数FailuresAccessor,文档中是这么介绍的:“是获取文档故障信息的唯一可以接口,虽然可以在故障处理期间读取文档,但在处理期间修改文档的唯一方法是通过此类提供的方法”。
但在我的测试中,拿到Document进行一些操作,没有效果也不报错,不明白🙃。
甚至只要我在故障处理方法力稍稍做些事儿,错误弹框就无法被跳过,还是会弹出来。

类成员,就不列了。

2.2. 取消Revit 警告/错误 弹框

注意,错误不应该直接ResolveFailure,Revit可能会采用删除的方式处理。

internal class MyFailuresPreprocessor : IFailuresPreprocessor
{
    public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
    {
        IList<FailureMessageAccessor> failures = failuresAccessor.GetFailureMessages();

        foreach (FailureMessageAccessor failure in failures)
        {
            //ICollection<ElementId> additionalElements = failure.GetAdditionalElementIds();  // 获取与问题相关的其他元素
            //ICollection<ElementId> failingElementIds = failure.GetFailingElementIds();  // 获取引起问题的元素

            FailureSeverity failureSeverity = failure.GetSeverity();  // 获取失败的严重程度

            if (failureSeverity == FailureSeverity.Warning)
            {
                failuresAccessor.DeleteWarning(failure); // 删除警告
            }
            else if (failureSeverity == FailureSeverity.DocumentCorruption)
            {
                return FailureProcessingResult.ProceedWithRollBack;  // 文档损坏,回滚
            }
            else if (failureSeverity == FailureSeverity.Error)
            {
                //FailureDefinitionId failureId = failure.GetFailureDefinitionId();  // 获取失败的定义ID
                //if (failureId == BuiltInFailures.CutFailures.CannotCutInstanceOut)  // 如果问题是无法剪切实例
                //{
                //    failure.SetCurrentResolutionType(FailureResolutionType.MoveElements);  // 尝试采用移动物体的方式处理
                //}

                failuresAccessor.ResolveFailure(failure);  // 解决错误
            }
        }

        return FailureProcessingResult.ProceedWithCommit;  // 提交  // 可能会删除部分模型
        //return FailureProcessingResult.Continue;
    }
}

上面的代码,会不显示所有的警告弹框。对于下面的错误弹框,也会消失。
当然,这不意味着就可以剪切了,具体的效果就是取消了剪切。
在这里插入图片描述


三、通过代码进行历史操作回退

在Revit左上角快捷栏里,有事务历史,我们当然可以通过交互的方式快速回退到之前的文档。其实这个操作也是可以通过代码进行的。
我们需要引入库UIFrameworkServices.dll,其中有类QuickAccessToolBarService

// 找到历史操作记录
public static ObservableCollection<string> collectUndoRedoItems(bool bForUndo);

// 回退或前进,步数
public static void performMultipleUndoRedoOperations(bool bForUndo, int iNumOperations)

总结

写到这儿,一看最上方的导图,好像还有个事件,算了,不写了🙄。

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值