What is the difference between "scope", "context", etc. in Ninject?
Asked Answered
N

1

6

Can the Ninject concepts of scope, context, named binding, (and activation block?) be separated and explained clearly at a conceptual level?

As an example, I have a service that loads data records from a database and for each record it constructs a "worker" via the Ninject factory extension. Both the service and the individual workers use Entity Framework's object context to interact with the database. The ObjectContext is injected via constructor to both (as well as are other shared dependencies). Currently it is single threaded, but eventually the workers need to run in their own threads in parallel and so they will need their own ObjectContext instance and explicit start/dispose life cycle. The ObjectContext instance needs to be shared for duration of the worker's "unit-of-work" (and so is not transient because it gets injected in to multiple repositories used by the worker). I am stuck trying to get this functionality.

I naively wanted something like this (using the named scope and context preservation extensions):

Bind<MyDbContext>().ToSelf();
Bind<MyService>().ToSelf();

Bind<IWorkerFactory>().ToFactory().InThreadScope();  // scope prob not necessary

Bind<MyWorker().ToSelf().DefinesNamedScope("workerScopeName");
Bind<MyDbContext>().ToSelf().InNamedScope("workerScopeName");

That obviously (at least obvious to Ninject users) results in "More than one matching binding..." error due to the MyDbContext. After reading a lot more, I now think I should probably be using named bindings for the worker and it's ObjectContext. I think I still also need the scope so that I can explicitly dispose the ObjectContext when the worker has finished (and has it's dispose method from ninject scope handling).

In any case, I'm still mostly guessing and I am posting this question in hopes someone can clarify these concepts in Ninject.

Neutrality answered 29/1, 2013 at 23:6 Comment(0)
N
9

Context: Meta information for the current resolve. It specifies where in the object tree the currently resolved object will be injected into. (e.g. The currently resolved object will be injected into the constructor of class A, which is injected into class B, ....) It can be used for example to decide if a binding applies in the current context using one of the When overloads. It is passed to many other callbacks of the fluent syntax as well (e.g. InScope, OnActivation, ....)

Scope: Defines the lifecycle of an object and when to reuse an existing instance, There are many predefined scopes as well as a generic one which can specify the scope from the current context (InScope(ctx => ...)

Named Binding: Meta Information on the binding. Can be used in combination with context. E.g. The binding only applies when in the current context any of the parent bindings has some name.

Activation Block: (As of Ninject 2.x - 3.x, this will likely change in future versions). Within an activation block each bindings is changed to have the activation block as scope. This means the scope specified on the binding will be ignored. Exactly one instance will be created for resolves on one activation block.

I personally wouldn't use that feature because it is flawed by ignoring all kind of other scopes like InSingletonScope. Better use the scopes given by Ninject.Extensions.NamedScope.

Regarding your example: Since you have two MyDbContext bindings you will need to add conditions to them. e.g. WhenAnyAncestorNamed. Or you could use another Scope like InCallScope() with just one binding.

Nester answered 30/1, 2013 at 11:29 Comment(1)
Thanks, this helps. Can you add information regarding "activation blocks" and how they relate (or not) to context and scope? (Also, future readers of this answer might find a reference to child kernels would make this answer complete.) I've been all through the ninject wiki, but it's difficult to determine what is current or not.Neutrality

© 2022 - 2024 — McMap. All rights reserved.