Skip Invalid Element Generation Using Failure API
Originally Published inWe already took a couple of looks at the Failure API in the past, e.g.:
Here is a slightly more convoluted situation that motivates revisiting the topic from a new angle, raised and solved by Stephen Faust of Revolution Design, Inc. ↗ together with Arnošt Löbel of the Revit API development team, who also invested significant time and research in the issue:
Question: I am generating some floor objects from rooms in a project. In certain cases the sketch is not valid because of some oddities in the rooms. This is OK if I can just delete or not create the floors and hopefully not show a dialogue to the user. Without any failure handling it gives the user the ‘can’t make extrusion’ error, but you can hit delete elements and be on your way. I thought I could do this with a failures preprocessor, but it doesn’t seem to be working the way I hoped. Here is the functional part of my class that inherits IFailuresPreProcessor:
IList<FailureMessageAccessor> fmas
= failuresAccessor.GetFailureMessages();
if( fmas.Count == 0 )
{ return FailureProcessingResult.Continue; }
foreach( FailureMessageAccessor fma in fmas )
{ failuresAccessor.ResolveFailure( fma ); }
return FailureProcessingResult.ProceedWithCommit;
When I use this with the transaction, it still gives me an error dialog, only this time it shows (Deleted) next to all of the elements. The only thing I can do is cancel, which is actually worse, because then you don’t get any of the floors. What am I doing wrong here? What do I need to do to just handle the dialog and tell it to remove those floors that aren’t valid?
Answer: What you probably need to do is to set the resolution on each failure.
There are also some checks required to avoid attempting to resolve a failure that may not be resolved (anymore).
I suggest the following starting point for an approach, plus further experimenting with it:
- Test if invalid elements (floors) can actually be deleted by calling FailuresAccessors.IsElementsDeletionPermitted.
- Possibly, for each failure, it may be tested if its resolution by deleting the invalid elements is permitted. The method to do so is FailuresAccessors.IsFailureResolutionPermitted taking a FailureResolutionType.DeleteElements argument.
- Assuming you can, failures should be resolved by FailuresAccessors.ResolveFailures.
- I am also guessing that before returning ProceedWitCommit, FailuresAccessors.IsTransactionBeingCommitted is tested.
- Return ProceedWitCommit.
Response: Thanks for the tips.
Here is what I ended up doing that seems to be working:
if( failuresAccessor.GetTransactionName()
== "Create Temporary Roofs" )
{
IList<FailureMessageAccessor> fmas
= failuresAccessor.GetFailureMessages();
if( fmas.Count == 0 )
{ return FailureProcessingResult.Continue; }
foreach( FailureMessageAccessor fma in fmas )
{
FailureSeverity s = fma.GetSeverity();
if( s == FailureSeverity.Warning )
{ failuresAccessor.DeleteWarning( fma ); }
else if( s == FailureSeverity.Error )
{
if( failuresAccessor
.IsElementsDeletionPermitted(
fma.GetFailingElementIds().ToList() ) )
{
failuresAccessor.DeleteElements(
fma.GetFailingElementIds().ToList() );
}
failuresAccessor.ResolveFailure( fma );
}
}
return FailureProcessingResult.ProceedWithCommit;
}
else
{ return FailureProcessingResult.Continue; }
Basically I separated out warnings from errors. Then warnings can be deleted, errors can be processed and delete elements can be directly called for specific elements causing the error.