Stable Composition in MEF Preview 6

image

Nick just posted on a new and very important feature we added in Preview 6 which we call Stable Composition. To give you an idea of why we added this, in the past if you had a part, say OrderProcessor, that had a required import of ILogger, and no logger was present, then composition would fail, and in so doing would corrupt the state of the container. Once that corruption happened, you were basically left with no option but to tear down the container, and in most cases shutdown the application. With Stable Composition the troublesome parts are rejected from the system thus maintaining stability.

In summary, it basically has two components to it:

First, it ensures that only parts whose imports can be satisfied will ever be allowed to enter the system. Those imports might not be satisfied for one of several reasons including the export not being present (Importing ILogger and no loggers are present), or too many exports being present (Importing ILogger and many loggers are present). Note: You can still use the approach described here to provide defaults in the presence of multiple implementations.

Second, this feature ensures that once parts have entered the system, they will never be allowed to enter an invalid state (i.e. their dependency requirements will not be able to be upset). For example if a part in a running system contains an import of a single logger, and one attempts to add a new logger to the catalog/container, then that new logger will be rejected.

In either case, the effect is to reject such parts and their transitive dependencies, meaning the entire object graph that is problematic is pruned. In Nick’s example, A PointOfSale part imports ItemLookup contracts. ProductLookup depends on ProductRepository which is preset, so it is imported without a problem. ServiceLookup however depends on ServiceRepository which the standard POS SKU does not provide, thus ServiceLookup is rejected.

Subtleties of rejection

Now having such a feature present has some subtle implications in particular when the hierarchy of dependencies is deep, and the part at the bottom of the chain is rejected. For example, Shell imports Toolbar, and Toolbar imports the ToolbarButtonState service which is missing some dependency for one reason or another, then the Shell itself will be rejected. If you are debugging the application, you might be staring at the debugger dumbfounded when you see the message “No Exports Found” for Shell. The problem has nothing to do with Shell directly, but it is related to one of it’s deep dependencies.

There are ways to design around this, for example if Toolbar is an optional import on Shell, then Shell will load even if Toolbar is not present. Thus you could check the Toolbar property, determine it is null and throw an exception indicating that there is an issue with the Toolbar or one of it’s dependencies.

Nick has posted an indispensable tool which he discusses in the Preview 6 post which analyzes the container to determine which parts have been rejected and why.  Finally in our next drop, Preview 7, (and in Beta 2) you’ll see we’ve added tracing infrastructure that in the debugger will write part rejection messages to the output window. You will also be able to use the tracing to write to a log file at runtime for diagnosing rejection in a deployment environment.

We are planning further guidance on Stable Composition which will ship with our documentation.

Check out Nick’s post for more details on the feature, and as always feedback is appreciated.