DynamoDS/Dynamo — pull request #2477 by Benglin (2014-09-19 03:29:47+00:00)
Title: Unification of Dynamo shutdown sequence

# Background
This pull request is meant to unify the way ```DynamoModel``` is shutdown (more specifically, ```RevitDynamoModel```). During the shutdown process, ```DynamoModel.ShutDown``` takes charge of the sequence in which virtual methods like ```PreShutdownCore```, ```ShutdownCore``` and ```PostShutdownCore``` are called. ```DynamoModel.ShutDown``` method also raises events like ```ShutdownStarted``` and ```ShutdownCompleted``` at very specific points in the shutdown process, allowing more deterministic event handling on higher layers like ```DynamoViewModel```.

# Changes
### Existing shutdown behavior
##### Shutdown sequence 1 (an ideal scenario):

1. DynamoView.WindowClosing > RevitDynamoModel.ShutDown
2. RevitDynamoModel.ShutDown > IdlePromise.ExecuteOnShutdown (delete Keeper ElementId)
3. RevitDynamoModel.ShutDown > DynamoModel.ShutDown
4. DynamoModel.ShutDown
5. RevitDynamoModel.ShutDown > IdlePromise.ExecuteOnIdleAsync (PostableCommand.ExitRevit)
6. DynamoRevit.DynamoView_Closing > IdlePromise.ClearPromises
7. DynamoRevit.DynamoView_Closing > IdlePromise.Shutdown
8. DynamoRevit.DynamoView_Closed
9. Shutdown delegate (from Step #2 above)

##### Shutdown sequence 2 (incorrect scenario):

1. DynamoView.WindowClosing > RevitDynamoModel.ShutDown
2. DynamoRevit.DynamoView_Closing > IdlePromise.ClearPromises
3. DynamoRevit.DynamoView_Closing > IdlePromise.Shutdown
4. DynamoRevit.DynamoView_Closed
5. RevitDynamoModel.ShutDown > IdlePromise.ExecuteOnShutdown (delete Keeper ElementId)
6. RevitDynamoModel.ShutDown > DynamoModel.ShutDown
7. DynamoModel.ShutDown
8. RevitDynamoModel.ShutDown > IdlePromise.ExecuteOnIdleAsync (PostableCommand.ExitRevit)
9. Shutdown delegate NOT executed (#5 comes after #3)

There are various ways a shutdown sequence can be initiated:

1. Pressing ```X``` button on Dynamo window (sequence 1)
2. By update manager (sequence 2)
3. File > Exit menu item (sequence 2)

### Unified shutdown process
The new shutdown process starts with a call to ```DynamoViewModel.PerformShutdownSequence```. Regardless of the caller (i.e. the ```Shutdown Triggers```), it performs the shutdown in the following order:

Shutdown Triggers > DynamoViewModel.PerformShutdownSequence
PerformShutdownSequence > DynamoModel.ShutDown
DynamoModel.ShutDown > OnShutdownStarted
OnShutdownStarted > IdlePromise.ExecuteOnShutdown (delete Keeper ElementId)
DynamoModel.ShutDown > RevitDynamoModel.PreShutdownCore
RevitDynamoModel.PreShutdownCore > IdlePromise.ExecuteOnShutdown (PostableCommand.ExitRevit)
DynamoModel.ShutDown > RevitDynamoModel.ShutdownCore
RevitDynamoModel.ShutdownCore > DynamoModel.ShutdownCore
DynamoModel.ShutDown > RevitDynamoModel.PostShutdownCore
RevitDynamoModel.ShutDown > IdlePromise.ClearPromises
RevitDynamoModel.ShutDown > IdlePromise.Shutdown
Shutdown delegates (from 'ExecuteOnShutdown' calls above)
DynamoModel.ShutDown > OnShutdownCompleted
DynamoRevit.DynamoView_Closed

Interesting facts about this shutdown process:

1. If the shutdown process should also close Revit, ```PostableCommand.ExitRevit``` will be sent through ```IdlePromise.ExecuteOnShutdown``` (instead of ```ExecuteOnIdleAsync```), ensuring that it does not get cleared by ```IdlePromise.ClearPromises```.
2. ```DynamoModel.ShutdownStarted``` event is added and called right before ```PreShutdownCore```.
3. ```DynamoModel.ShutdownCompleted``` event is added and called right after ```PostShutdownCore```.

# Testing scenarios

1. Tested both shutdown delegates are executed regardless of the ```Shutdown Triggers```.
2. Tested the following scenarios for both ```Revit 2014``` and ```Revit 2015```:
- Pressing ```X``` button on Dynamo window
- Pressing ```CTRL + F4``` shortcut key
- Fake update manager call to install update
- ```File > Exit``` menu item