5

I'm using Ninject to instantiate some objects with a constructor arg passed, e.g.:

class MyClass
{
 public MyClass(string myArg)
 {
 this.myArg = myArg;
 }
}

The number of instances I need of this class won't be known until runtime, but what I want to do is ensure that each variation of myArg results in a different singleton instance (so asking for the same value twice returns the same instance, but different args return different instances).

Does anyone know of a good, preferably built-in, way of doing this?

I found an article written for an older version of Ninject How To Ensure One Instance per Variation of Activation Parameters but was hoping there'd be a tidier solution for the newer version.

Edit

Here's what I went with, adapted from Akim's answer below:

private readonly ConcurrentBag<string> scopeParameters = new ConcurrentBag<string>();
internal object ParameterScope(IContext context, string parameterName)
{
 var param = context.Parameters.First(p => p.Name.Equals(parameterName));
 var paramValue = param.GetValue(context, context.Request.Target) as string;
 paramValue = string.Intern(paramValue);
 if (paramValue != null && !scopeParameters.Contains(paramValue))
 {
 scopeParameters.Add(paramValue);
 }
 return paramValue;
}
public override void Load()
{
 Bind<MyClass>()
 .ToSelf()
 .InScope(c => ParameterScope(c, "myArg"));
 Bind<IMyClassFactory>()
 .ToFactory();
}
asked Jan 24, 2013 at 13:24
1
  • Check ninject.extensions.factory for automatic implementation of abstract factory Commented Jan 24, 2013 at 16:10

1 Answer 1

3

You could achieve require behaviour by providing custom scope using IBindingNamedWithOrOnSyntax<T> InScope(Func<IContext, object> scope) method for MyClass binding

Indicates that instances activated via the binding should be re-used as long as the object returned by the provided callback remains alive (that is, has not been garbage collected).

So, you need to return value of first constructor argument from Func<IContext, object> scopeand make sure that would not collect it.

Here is a snippet:

public class Module : NinjectModule
{
 // stores string myArg to protect from CG
 ConcurrentBag<string> ParamSet = new ConcurrentBag<string>();
 public override void Load()
 {
 Bind<MyClass>()
 .ToSelf()
 // custom scope
 .InScope((context) =>
 {
 // get first constructor argument
 var param = context.Parameters.First().GetValue(context, context.Request.Target) as string; 
 // retrieves system reference to string
 param = string.Intern(param);
 // protect value from CG
 if(param != null && ParamSet.Contains(param))
 {
 // protect from GC
 ParamSet.Add(param);
 }
 // make Ninject to return same instance for this argument
 return param;
 });
 }
}

ps: full sample code with unittests

answered Jan 24, 2013 at 14:54
Sign up to request clarification or add additional context in comments.

3 Comments

Really like how straightforward this solution is, and I got a bit of extra help on the wiki at github.com/ninject/ninject/wiki/Object-Scopes. The instances will need to come from a factory though, do you know if this will be compatible with the Factory Extension? I assume it will be.
I tidied up your example a bit in my code so that I could tell it which parameter to use instead of always using the first one and I wrote some tests. Works perfectly, thanks!!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.