InNamedScope

bartelink edited this page Aug 1, 2013 · 10 revisions

The notion of a Named Scope is the most general and powerful facility offered in this Extension -- the other facilities are actually implemented using the Named Scope mechanism.

A Named Scope lets you specify on a Binding that objects created via the Binding are to act as the Scope Object for other objects that are generated as part of the work of Composing the object graph for a given Get<T>() Resolve Request.

The facility consists of two Extension Methods

  • DefinesNamedScope(string scopeName). This identifies the Binding as creating a Scope which can be referred to in other Bindings. Only a single instance of a service scoped in this manner will be created per Object Graph being Composed (each Composition in the Composition Root (e.g. via Kernel.Get<T>() will generate a separately isolated instance)
  • InNamedScope(string scopeName). This constrains all resolutions of the Service Type being bound to produce a single shared instance which will be yielded as part of any Resolve within a given Object Graph Composition. (TODO verify) Note that an Exception will be thrown if a Resolve has not yet had cause to visit a Binding that employs DefinesNamedScope() to establish the Named Scope with the cited scopeName earlier in the Object Graph Composition.

Scenario 1: Sheet implementation with collaborating components sharing a single Sheet Data Repository per Sheet object within a single Object Graph Composition

Lets examine how this works using an example scenario. Imagine that you are creating an Excel-like application that has multiple worksheets. As part of the implementation, you have multiple components (e.g. the data repository) that have a worksheet as their Scope and need to be accessible to several other components.

To be more concrete you might have a Sheet implementation which relies on two collaborating components -- one to draw the sheet (SheetPresenter) and one to update the calculated values on each cell (SheetCalculator). These two parts of the overall Sheet code each have a dependency on SheetDataRepository and the same instance needs to be supplied to each one. One can’t simply Bind...().To<SheetDataRepository>().InSingletonScope() because you need to be able have multiple sheets in the system. In this case the Bindings can be specified as follows in order to have the Sheet instance act as the Scope relative to which a single SheetDataRepository instances is created and shared for subsequent usages within the Object Graph being Composed:

const string ScopeName = "Sheet";
Bind<Sheet>().ToSelf().DefinesNamedScope(ScopeName);
Bind<SheetDataRepository>().ToSelf().InNamedScope(ScopeName);
Bind<SheetPresenter>().ToSelf();
Bind<SheetCalculator>().ToSelf();

Scenario 2: Deferred Creation of Services via Factories that require Shared Objects from the Initial Object Graph

In conjunction with Ninject.Extensions.ContextPreservation this Scope type can also be used for instances that are not created as direct dependency within a single Object Graph Composition, but at a later point in time by a (possibly generated) Factory that was Resolved as a dependency in the initial Object Graph Composition.

Extending Scenario #1, lets say SheetCalculator gets a Factory (CellCalculatorFactory) injected with a CreateCellCalculator() method that is used by the SheetCalculator to create a CellCalculator whenever a formula is added to a cell. However, the CellCalculator of course needs to gain access to a SheetDataRepository -- the single one that was generated in the initial Object Graph Composition.

This scenario can be modelled using the following Binding structure:

this.kernel.Load(new NamedScopeModule());
this.kernel.Load(new ContextPreservationModule());
 
const string ScopeName = "Sheet";
Bind<Sheet>().ToSelf().DefinesNamedScope(ScopeName);
Bind<SheetDataRepository>().ToSelf().InNamedScope(ScopeName);
Bind<SheetPresenter>().ToSelf();
Bind<SheetCalculator>().ToSelf();
Bind<CellCalculatorFactory>().ToSelf();
Bind<CellCalculator>().ToSelf();

Related