Well, that’s simple. The idea itself comes from the CAB.

Basically you can organize your application (whether it is a desktop client, an automation engine, web UI or something else) in such a manner:

This sample desktop application is logically organized into the nested scopes (or contexts or containers).

The Application Scope is the top-most. It contains component registrations that are associated with the entire application (user identity, service for displaying nice message boxes, navigation service, data layer, different commands, etc). The components are accessible by any child scope.

Let’s say that the application consists of workspaces (just like in cab) that are designed to hold views (i.e.: tabbed workspace displays views in tabs, window workspace creates a separate window for each view, etc). Workspaces can declare their own scopes that could hold items common to all views within.

Then, every view could have one more scope that contains components specific to this view.

What’s the point of that?

Well, this is simple. Imagine, that you follow the command pattern and encapsulate specific actions (i.e. delete record, add record, print grid, export grid etc) in separate command classes. And you register these classes by name within the Application scope, since you want to be able to access them from any scope.

Simplified example of such command would be:

public sealed class DeleteSelectedRecords : ICommand
{
  private readonly ISession _session;
  private readonly IRecordSelector _selector;
  private readonly IMessageBoxService _messaging;

  public DeleteSelectedRecords(IRecordSelector selector, ISession session,
    IMessageBoxService messaging)
  {
    _session = session;
    _selector = selector;
    _messaging = messaging;
  }

  public void Execute()
  {
    if (_messaging.Ask("Are you sure about that?") == MessageCode.OK)
    {
      _session.Delete(selector.GetRecords());
    }   
  }
}

Now:

  • If you resolve this command in the scope of some GridView view (i.e. viewScope.ResolveByName), the appropriate constructor parameters will be populated automatically.
  • IRecordSelector and ISession are pulled from the current context while IMessageBoxService belongs to the entire application (and the instance is declared only there).
  • The command is reusable in any scope that has these three components.
  • This specific command is stateless and can be reused. So if it was registered with the container ownership and scope, then the first request will create the command within the container, fill it with the dependencies and return the instance. All subsequent requests will merely return the existing command.
  • Since the commands are referenced by name, it is easy to replace or customize any single command specifically.
  • It is possible to override some behavior in the nested containers simply by declaring override instance of some component. For example, if you need to open views in specific workspace with the access rights of the guest account (i.e. for preview), then simply add IUserIdentity for the guest into that workspace. And all commands that use this component will get the override.
  • How do you add completely new command to an existing view in this simplified situation?
  • Code in some ICommand implementation.
  • Register that command within the Application Scope with some name (you do register components via configs, do not you?).
  • Add the name of the command to the list of commands that could be called by the specific view (you keep that in configs, too, do not you?)
  • Sit back and relax. The rest will be done by the IoC container and the view.
  • The scenario above is a bit simplified. I’ve skipped the command registration (command links) within the menus in a menu-invariant manner (whether it is a global Ribbon with dynamic tabs, local group within BarManager or something else).
  • The scenario above can be implemented without touching the original codebase. You just need to reference one more assembly and then tweak the settings.
  • ICommand is just one example. You could also register other components (i.e.: IView, ITranslator, IWorkflow, IController etc) in similar manner. Every specific usage scenario could have its specifics, yet the common benefits are shared.
  • All scope-related components will be disposed when the container is disposed.
  • Your IoC container has to support the proper nested scopes and determenistic disposal, in order to pull these tricks. Autofac does that.
  • That’s how it is advised to be done for the current implementations of xLim 2.