上一篇文章,我们在讲到Revit二次开发怎么Show出HelloWorld窗口的时候,就简单地提了一下事务模式,但没有深究其内容,所以在这里我们再展开讲解一下关于事务的内容。
事务的模式
首先,在HelloWorl一文中,我们讲到每个外部命令类的前面都需要加Transaction特性,而这个特性实际是声明外部命令的事务模式。 在过去的API版本中,事务模式一共有三种,分别是Manual、ReadOnly和Automatic,但因为Automatic在后续的版本被舍弃了,所以我们重点关注前两种事务模式即可。① Manual:手动开启事务模式。只要我们的命令涉及到修改文档数据,那就一定是选用这个模式。但声明了这个模式,不去修改文档数据也不会有什么问题(只是会占用资源)。[Transaction(TransactionMode.Manual)] ② ReadOnly:只读模式。这个模式只能用在不修改文档数据的外部命令,如果选用了这个模式,然后在执行的时候去修改文档,程序就会报错。[Transaction(TransactionMode.ReadOnly)] ③ Automatic:自动开启事务模式(因为被舍弃了,所以可忽略)。Revit在执行外部命令的时候自动开始事务,然后根据外部命令返回的结果来决定事务提交还是回滚。[Transaction(TransactionMode.Automatic)] 通过上面的介绍,我们可以发现HelloWorld一文中的外部命令,其实声明ReadOnly就可以了,因为弹出一个窗口并不涉及到修改rvt文档的数据。
事务的类型
如果我们的命令不需要修改文档数据,那么声明了事务模式后就不需要再关注事务类型,但如果我们的命令需要修改文档数据,那么下面的内容就必须了解。虽然我们已经声明了Manual的事务模式,但在涉及到修改文档数据的语句前,我们还需要声明并开启事务:Transaction transaction = new Transaction(doc, "Hello World"); transaction.Start(); 如果不想提交,那就回滚事务。但这样做,之前的修改就无效了:可以说只要涉及到修改文档数据,这些就是必须写的语句。要么提交事务,要么回滚事务。API提供的事务类型并不仅仅是上面写的这一种,还包括TransactionGroup和SubTransaction,它们三者之间是包含与被包含的关系(TransactionGroup包含Transaction,Transaction包含SubTransaction)。 ① TransactionGroup:事务组。声明并开启了事务组后,程序可以在执行不同的修改时声明并开启不同的Transaction,但这些Transaction都必须在事务组提交之前提交。 ② Transaction:上面已经介绍过了,只要涉及到修改文档数据的地方都必须开启它,然后提交事务或回滚事务。 ③ SubTransaction:这个是子事务。它必须在Transaction开启事务后开启,然后在Transaction提交事务前提交。同样,它可以在Transaction里面开启并提交很多次,也可以在SubTransaction继续嵌套下去,如果我们的程序有需要的话。 看到这里,我们可能会想,当我们在修改文档前声明并开启了Transaction,修改完后提交Transaction不就完事了?还需要什么事务组、子事务? 一般来说,确实如上所述。 但我们在开发比较复杂的功能的时候,可能会遇到这样一种场景。我们希望在修改文档数据的时候先修改一部分内容,然后再做别的操作(如开启族文件),然后再修改另一部分内容。但又希望程序提交完事务后,不要在Revit中出现很多操作记录。 这时候通过事务组提供的事务合并方法“Assimilate”,我们就能轻松解决这个问题: 上图中的程序,Revit执行完后只会产生一个叫“事务组”的操作记录,而不会分别出现“事务1”和“事务2”。 而另一种情形。在我们开启了Transaction后,需要修改较多内容,而每个内容需要根据前面的修改结果再去修改后面的内容,这时候子事务就派上用场了。 这里第二个子事务,我们调用了回滚事务的方法。最终,提交Transaction,Revit只会修改第一部分的内容,第二部分的修改因为回滚而无效。同样的,在上述任何一个提交的地方,我们都可以替换成回滚,而回滚只会导致其内部的修改、事务或子事务的提交无效,而不会影响其他地方。关于事务的内容,我们就先介绍到,切记修改文档数据一定要开启事务~
|