How to use the additional Ninject Scopes of NamedScope

The Named Scope extension adds the three additional Scopes InCallScope, InNamedScope and InParentScope to Ninject. In this Blogpost I want to explain how they are used. Some parts of this post expect that you have a basic knowledge about the Context Preservation extension. Consider reading my blog post about the Context Preservation Extension before reading this one.

InNamedScope

The named scope is the most powerful scope of all three scopes. In Fact the other two are using named scope for their implementation. The named scope lets you define on a binding that the object created by the binding is the scope for other objects that are part of the object tree that is injected into the created object.

Let us see how this works on an example. Imagine that you are creating an Excel like application that has multiple worksheets. In this case you have multiple components (e.g. the data repository) that have the worksheet as scope and are accessed by several other components.

To be more concrete you have a ExcelSheet that has two components one to draw the sheet and one to update the calculated values on each cell. These two parts of the sheet have a dependency on SheetDataRepository and the same instance shall be used for them. It can’t be singleton because you want to be able to create multiple sheets. In this case the bidings can be defined like this to have the worksheet as the scope:

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

In combination with Ninject.Extensions.ContextPreservation this scope type can also be used for bindings that are not created as direct dependency, but sometime later by a factory that was created as dependency. Imagine in the scenario above that the SheetCalculator gets a factory CellCalculatorFacotry injected. This factory has a Method CreateCellCalculator that is used by the sheet calculator to create a cell calculator whenever a formula is added to a cell. The cell calculator needs a SheetDataRepository of course.

  this.kernel.Load(new NamedScopeModule());
  this.kernel.Load(new ContextPreservationModule());

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

InCallScope
If this type of scope is added to a binding only one instance is created for one call to kernel.Get(). It is basically a less powerful version of InNamedScope that automatically adds a named scope to the object requested by the Get call. But it stops at factories even when used togetter with context preservition. In the above scenario the first example can be written as

  Bind<ExcelSheet>().ToSelf();
  Bind<SheetPresenter>().ToSelf();
  Bind<SheetCalculator>().ToSelf();
  Bind<SheetDataRepository>().ToSelf().InCallScope();

But the second scenario is not possible because the CreateCellCalculator requests on the factory would produce a new data repository for each call. There is one situation where InCallScope is transparently passed through a Get when Context preservation is used. Imagine that in the above scenario we have two different views onto the data repository using two interfaces. ICellValues and ICellFormulas. In this case the bindings look like this:

  Bind<ExcelSheet>().ToSelf();
  Bind<SheetPresenter>().ToSelf();
  Bind<SheetCalculator>().ToSelf();
  Bind<SheetDataRepository>().ToSelf().InCallScope();
  Bind<ICellValues>().ToMethod(ctx => ctx.ContextPreservingGet<SheetDataRepository>());
  Bind<ICellFormulas>().ToMethod(ctx => ctx.ContextPreservingGet<SheetDataRepository>());

For the last two bindings the short form can be used of course

  kernel.BindInterfaceToBinding<ICellValues, SheetDataRepository>();
  kernel.BindInterfaceToBinding<ICellFormulas, SheetDataRepository>();

InParentScope
This is basically the same as InTransientScope() at least when it comes to the lifecycle and the decision which object is injected. As with InTransientScope a new instance is injected for each dependency. The difference to InTransientScope is that bindings with this scope will be deactivated after the object that gets the instance injected is collected by the garbage collector. E.g. the container disposes the object when it is not used anymore.

public class Foo
{
    public Foo(Bar bar) { ... }
}

public class Bar : IDisposable { ... }

Bind<Bar>().ToSelf().InParentScope();

In this example Bar gets disposed by ninject as soon as Foo gets collected ba the garbage collector.

Child Kernel versus Named Scope

Many situations allow the usage of the child kernel instead of named scope. In the first example it is possible to create a new child kernel for each sheet and declare the data repository as singleton. There are pros and cons to these two approaches.

Pro ChildKernel:

  • Guaranteed no side affects. e.g. if one binding is incorrectly defined there the various worksheets can not have any impact to each other. Imagine that you did the misstake to declare a dependency as InSingletonScope instead of InNamedScope. In this case the worksheets will share the instance and influence each other.
  • Allows two different types of whorksheets. If you have whorksheets that behave differently you can load a different set of bindings instead of doin conditional bindings. This is much easier in most cases.

Pro Named Scope:

  • Faster. Ninject will do the analysis of the classes only once and build up the plans on the first resolve. All further resolves won’t have to do the planning again.
  • Shared bindings can use the ones defined for the named scope. The child kernel has the restriction that bindings declared on the parent kernel can not access the bindings from the child kernel.

About the author

Remo Gloor

My name is Remo Gloor and Iā€™m working as a software architect at bbv Software Services AG in Zug, Switzerland. My first expiriences with computers were a a little boy with basic programming on a C64, later Pascal an C on an Intel 8080. After finishing my study in software engineering I started working with C#.

Currently, I'm one of two Main Developers of the well known Ninject IoC containers. My other interests beside Ninject are TDD, Scrum and software architecture in general.

6 comments

  • Hi Remo,

    nice Post! Actually i am facing some problems during my follow up in the In Named Scope section. The later part where you introduced the cell factory is actually the interesting part for me, where i’m getting lost as well :/.
    Could you please give some more information on the factory implementation especially the signature and the implementation of the factory method itself??

    I always come up with solutions where i would need to access the kernel, or is this already leading the right way?

    Thanks in advance
    Isaias

  • My current solution would look like the following, please let me know if this was the intended solution.

    Thanks in advance
    Isaias

    class CellCalculatorFactory
    {
    public IResolutionRoot resolutionRoot;
    public CellCalculatorFactory(IResolutionRoot resolutionRoot)
    {
    this.resolutionRoot = resolutionRoot;
    }
    public CellCalculator CreateCellCalculator()
    {
    return resolutionRoot.Get();
    }
    }

  • Hi Daniel,

    thanks for assistence!

    My first question belonged to the “InNamedScope” section of this blog post, more precisely to the loaction “In combination with Ninject.Extensions.ContextPreservation this…” where the factory is described.
    In my second comment i have implemented a factory meeting the given requirements, so that it can be bound to itself as mentioned in the example code.
    Line 8: Bind().ToSelf();
    This leads to a valid solution, with the right behaviour, which made the first question obsolete.

    My second question was, if implementing such a factory which somehow contacts the IoC (over IResolutionRoot) in order to retrieve the scoped instance of interest, would be the way to handle this specific problem. Or at least if the author had such an implementation in mind while designing this example.

    After some days of extremely hard thinking šŸ˜‰ i think that the above clarifications are already the answer to my own questions. Because any factory implementation for such a problem will need to locate the object of interest in the IoC!

    And the question was not addressing the Ninject.Factory extension.

    Thanks again
    Isaias

  • The context preservation extension is the important part combined with the named scope. Without the context preservation extension your get in the factory would start a new request. This would then not have the original named scope available.

By Remo Gloor

Recent Posts