In the three months that have passed since the last release, we’ve been very busy making final adjustments to get closer to RTM.
In this post I’ve summarized the biggest changes between the Preview 5 and Preview 6 releases.
The highlights are:
- Silverlight support
- Lazy<T> replaces Export<T>
- Collection import changes
- Inheritance changes
- Export attribute is unsealed
- Stable Composition makes its debut
You can download MEF Preview 6 from the CodePlex site.
With this release comes an early preview of our Silverlight plans. The included solution MEFSL.sln can be used to build MEF binaries that target the newly-released Silverlight 3.
Those familiar with the full-framework MEF won’t see many differences in the Silverlight build as it stands today. We aim to have some exciting new features in the eventual release, but for now the APIs are the same apart from changes required because of the different environment.
DirectoryCatalog is not available in the Silverlight version. Instead, this is replaced by PackageCatalog.
This API is tentative and will very probably change in a future preview.
Silverlight Picture Viewer
There’s a new sample called PictureViewer that demonstrates some aspects composition in a Silverlight application.
One of the first things you’ll notice when moving to the new build is that the System.ComponentModel.Composition.Export<T> and Export<T, TMetadata> types are gone.
In their place, MEF now recognizes the System.Lazy<T> and Lazy<T, TMetadta> types.
Functionally these types match the old ones, however:
- The GetExportedObject() method is replaced by a Value property
- The raw metadata dictionary is gone (if you truly require this, you can use the IDictionary<string, object> type as a metadata view interface)
Collection Import Changes
The System.ComponentModel.Composition.ExportCollection<T> type has also been removed. In its place, use IEnumerable<Lazy<T>>, or Lazy<T>.
PartExportsInheritedAttribute has been removed. The new mechanism separates inheritable exports from non-inheritable exports.
Exports declared using the ExportAttribute are never inherited.
Exports declared using the InheritedExportAttribute will be exposed by sub-classes of the part type.
Custom Export Attributes
In previous releases, ExportAttribute was sealed.
It is now possible to subclass ExportAttribute, and this can be combined with the existing MetadataAttribute functionality.
This means that instead of:
It is possible to define a custom attribute that completely describes the export:
Note: We have also unsealed ImportAttribute, ImportManyAttribute and ImportingConstructor thus allowing these to be extended as well.
As an extensibility framework, it is important that MEF applications start and execute reliably even when some extensions lack required dependencies.
Stable Composition contributes to this goal by validating dependencies in advance.
Part Definition Rejection
In earlier releases, MEF would hand out lazy exports from parts with missing dependencies. Later, when the export was used an exception would be thrown because of problems creating the part.
There were two major issues with this:
- Side-effects of failures during composition could not always be cleaned up
- Light-up could only be supported via optional dependencies, which led to awkward component designs
The solution to this problem is:
Parts that have missing required dependencies are ‘rejected.’ They appear in the catalog, but the container never uses them.
MEF remains dynamic – as parts are added and removed from the composition, the state of part definitions can change. Parts that may have been rejected will become available when their dependencies can be satisfied.
The container now treats lazy references as promises. Once the container hands out a Lazy<T> export, the value is guaranteed to be available, and to remain available for the life of the part that imports it.
The parts available in a MEF container can change, either by adding or removing to the a ComposablePartCatalog, or directly via CompositionContainer.ComposeBatch().
In order to keep promises, changes like these are examined carefully and verified not to break anything already instantiated.
If such a change would cause an existing promise to be broken, the change is rejected by means of a CompositionException.
Diagnosing Composition Problems
One of the implications of Stable Composition is that the rejected parts will simply never show up.
Because parts are interdependent, one broken part can cause a chain of other dependent parts to be rejected as well.
Finding the root cause of a ‘cascading rejection’ like this can be tricky.
Included in the samples under /Samples/CompositionDiagnostics is a prototype diagnostic library that can be used to track composition problems down.
The two basic functions that are implemented allow the rejection state of a part to be inspected, and the contents of an entire catalog to be dumped along with status and root cause analysis information.
Dump Composition State
For comprehensive diagnostics, the complete composition can be dumped in text format:
The output contains many interesting things, including ‘primary rejection’ guesses and analysis of common problems like mismatched type identity, mismatched creation policy, and missing required metadata:
There’s enough information here to correctly diagnose most common issues.
Find Likely Root Causes
The dump technique above is comprehensive but verbose, and if you have access to the running process in a debugger, the following is more likely to be convenient:
The return value of CompositionInfo.GetPartDefinitionInfo() is an object that gives quick access to all of the same analytical information as the text dump, but relating to the part Foo. The API exposes:
- The part’s rejection state (IsRejected)
- Whether it is a primary cause of rejection, or if it is rejected because of other cascading rejections (IsPrimaryRejection)
- Which parts are likely to be the root causes of the part’s rejection (PossibleRootCauses)
- The state of all imports (ImportDefinitions)
- For each import, which exports would satisfy the imports (ImportDefinitions..Actuals)
- For each import, which other exports with the same contract name were not matched, and the reason for each (ImportDefinitions..UnsuitableExportDefinitions)
Implementing Export Providers
The ExportProvider.GetExportsCore() method has gained a parameter to support Stable Composition. ExportProvider also has a new event ExportsChanging for the same purpose.
Simple ExportProviders that hand out the same values and do not recursively call into the container can ignore the parameter and event.
If you implement a custom ExportProvider that recursively calls into the container to satisfy dependencies of the exports it hands out, then you should participate in Stable Composition.
Likewise, if the set of available exports in an ExportProvider can change, you’ll need to fire the ExportsChanging and ExportsChanged events.
See the API documentation for these types for more details.
Other API Changes
A number of other supporting API changes have been made, and a few obsolete or problematic items removed:
- CompositionContainer.GetExportedObject() and related overloads have been renamed to GetExportedValue()
- CompositionEngine has been renamed ImportEngine
- AddExportedObject<T>() renamed to AddExportedValue<T>()
- ComposeExportedObject<T>() renamed to ComposeExportedValue<T>()
- New GetMetadataView<TMetadataView>() method
- New SatisfyIImportsOnce() method
- ICompositionService changes
- SatisfyImports() renamed SatisfyImportsOnce() and no longer registers the part for recomposition
- UnregisterForRecomposition() removed
- CompositionServiceExtensions removed
- ContractTypeAttribute removed since exports on interfaces are now inherited
- ComposablePartCatalogChangedEventArgs renamed to ComposablePartCatalogChangeEventArgs
- New AtomicComposition class supports Stable Composition
- Contract adapters have been removed
Help and Feedback
Enjoy the new release, and remember that the MEF discussion forum on CodePlex is a great place to get help if you find yourself stuck.
The MEF Team is very grateful to everyone who’s provided feedback and input into the new release. I hope we’ve hit the right note, but as always your comments are welcome.