ActivationBlock should only override scopes for transient objects

119 views
Skip to first unread message

dnagir

unread,
Dec 23, 2010, 10:07:11 PM12/23/10
to ninject
Hi,

I find the scope override with activation block is not really as good
as we I want it to be and probably we need other strategies.
Let's see an example (just use LinqPad):

class Counter {
public int Count;
}
class Singleton : Counter {}
class Scoped : Counter {}

void Main()
{
var scope = new object();
using(var kernel = new StandardKernel()){
kernel.Bind<Counter>().ToSelf().InTransientScope();
kernel.Bind<Singleton>().ToSelf().InSingletonScope();
kernel.Bind<Scoped>().ToSelf().InScope(x => scope);

kernel.Get<Counter>().Count ++;
kernel.Get<Singleton>().Count ++;
kernel.Get<Scoped>().Count ++;

using(var block = kernel.BeginBlock()) {
block.Get<Counter>().Count.Dump("Transient"); // 0 - Looks
Transient
block.Get<Counter>().Count++;
block.Get<Counter>().Count.Dump("No longer Transient (expected
0)"); // 1 - Transient? Nooo.

block.Get<Singleton>().Count++;
block.Get<Singleton>().Count.Dump("No longer Singleton (expected
2)"); // 1 - This is no longer singleton as scope was overwitten

block.Get<Scoped>().Count++;
block.Get<Scoped>().Count.Dump("No longer Scoped correctly
(expected 2)"); // 1 - This is no longer scoped as originally as scope
was overwitten

}

"FROM KERNEL".Dump();
kernel.Get<Counter>().Count.Dump("Transient (expected 0)"); // Ok
kernel.Get<Singleton>().Count.Dump("Singleton (expected 2)"); // 1 -
was not change inside the activation block
kernel.Get<Scoped>().Count.Dump("Scoped (expected 2)"); // 2 - was
not chenged in the activation block
}
}

THE OUTPUT:

Transient
0
No longer Transient (expected 0)
1
No longer Singleton (expected 2)
1
No longer Scoped correctly (expected 2)
1
FROM KERNEL
Transient (expected 0)
0
Singleton (expected 2)
1
Scoped (expected 2)
1





The problems are:

1. Transient need new instance every time, but is scoped to the
activation block.
2. Singleton was registered as a singleton, but it does not behave
like one in the ActivationBlock.
3. Scoped was rescoped to a new object.

This seems to just defeat the purpose of the registering scopes in
Kernel because all instances resolved from ActivationBlock will be
scoped to itself ignoring the originally defined scope. This is not
how I want Singleton to behave for example.

It is especially a concern in a web app where I currently use
ActivationBlock as a UnitOfWork so that I can dispose all objects at
the end of the request.

Unfortunately this cannot work because all objects are resolved
through ActivationBlock. Thus and all of them will be scoped to it.
This makes it impossible to properly create singletons and scoped
objects with ActivationBlock.

How I believe the activation block should behave is that only the
objects that have transient scope are REscopped to the
ActivationBlock.

Optionally, a callback to determine the rewrite rule for particular
case is useful. It's default implementation would do:

kernlel.BeginBlock(rescope => rescope.GetScope() ?? rescope)

I it should be very simple to implement it, but I am more concerned
about making such behavior a default one.

Regards,
Dmytrii.

dnagir

unread,
Dec 23, 2010, 10:41:28 PM12/23/10
to ninject
I just have to add that currently I workaround it using the method
below (which might be incorporated into Inject) where I explicitly use
UnitOfWork scope:

public class UnitOfWork : ActivationBlock {
public UnitOfWork(IResolutionRoot parent) : base(parent) {
}

public override IRequest CreateRequest(Type service,
Func<IBindingMetadata, bool> constraint, IEnumerable<IParameter>
parameters, bool isOptional, bool isUnique) {
return Parent.CreateRequest(service, constraint, parameters,
isOptional, isUnique);
}
}

public static class NinjectExtensions {
public static IBindingNamedWithOrOnSyntax<T>
InUnitOfWorkScope<T>(this IBindingWhenInNamedWithOrOnSyntax<T> self) {
return self.InScope(x => self.Kernel.Get<UnitOfWork>());
}
}


Then during registration:

// The UnitOfWorkScope
kernel.Bind<UnitOfWork>().ToMethod(x => new
UnitOfWork(x.Kernel)).InRequestScope(); // In the worst-case, let it
be disposed with HttpContext


kernel.Bind<ILowLevelRepository>()
.ToMethod(x => new
LowLevelRepository(x.Kernel.Get<ISessionFactory>()))
.InUnitOfWorkScope();

And at the end of request in a web app I do

kernel.Get<UnitOfwork>().Dispose();

Remo Gloor

unread,
Jan 3, 2011, 9:47:50 AM1/3/11
to ninject
You might want to look at my blog about additional scopes for ninject.
http://www.planetgeek.ch/2010/12/08/how-to-use-the-additional-ninject-scopes-of-namedscope/
I think thats a better way for your problem than using
ActivationBlock.
Reply all
Reply to author
Forward
0 new messages