Schema Manager encapsulates three different ideas:
- Container Inspector
- Configuration Manager
- Validation Test Runner
These three pieces are logically linked to each other, and because of that there's the synergy effect. It makes the resulting combination even more efficient and easy to implement.
Let's talk about the container inspector bit for a while.
The idea of the container inspector comes from the StructureMapExplorer and the CAB visualizer. Both had helped me a lot in understanding and visualizing what exactly happens within the multi-scoped applications (and why does this happen).
In this solution the container inspector is responsible for retrieving and displaying the information about the "Loaded Modules" sub-tree. It is implemented for the autofac as a simple ContainerInterceptor class that allows us to capture all the calls to the container with IComponentRegistration data. Then, we simply take all modules to be loaded and run them against this container interceptor one by one. And then we just take note of every registration that takes place (just 100 lines of code to do that).
Note, that the interceptor has to forward the calls to the real container, since modules are actually loaded in this virtual environment. And within the load process it is a common thing for some extension module to registers item in the service provided by the core module (or augment it).
Additionally the Container Inspector allows us to run one more validation check on the composite schema, just to make sure that the components and services that are referenced there actually exist. This is useful for the cases where modules do not create their schema bits on-the-fly, but rather preload existing schema bit from the resources. Of course the SchemaManager would detect the broken links and throw exceptions, but who wants to debug the logics when you can see only one error at a time? That's simply inefficient.
With a little bit of imagination one can make the Container Inspector more powerful than this by:
- Providing short descriptions for the modules and services via the attributes (usable when the modules are distributed to the other developers)
- Grouping all named implementations by the supporting interface(s) (it is not needed within xLim 2, since the composite schema handles this in a flexible way)
- Running validation tests for the components that support this (we'll get to that later)
- Exploring the properties of any given module, letting you to configure them and creating *.config file for the loaded modules.
- Linking the upper container to NMock2 (we'll talk about the MockContainer approach a bit later) attempting to resolve any given component (that's one of the reasons not to put any scope-specific logic into the constructor), reporting all container dependencies for the component including these that are not satisfied by the root application scope (that's the scope the modules get loaded into).
The last item would probably be helpful for xLim quite soon since a lot of modules do that kind of trick:
- Register scope-specific items that use some new service
- Register that new service in the root application scope.
Now, what happens, when the scoped item get resolved within some child scope? It pulls that service right from upper scope and would fail if it is not there. That's the logical dependency. If the service is registered within the same module with the scoped component, then it is easy to remember and manage. But if the service is registered within one module (i.e.: IExtensionWorkflow32 from Extension.Core) and then other components depend on it (i.e.: ExtensionDesktopView from Extension.Client and ExtensionWebView from Extension.Web), then we'd better to explicitly visualize these dependencies (and optionally auto generate the module documentation for that).
Imagine, if the Model Editor for the DevExpress eXpressApp Framework (XAF) supported this bit of functionality. Wouldn't it make the task of writing and managing the extension modules just a little bit easier?