2013-01-24 7 views
5

ich Ninject bin mit ein paar Objekte mit einem Konstruktor arg instanziiert geben, zB:Ninject Memoize Instanzen in Singleton Scope

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

Die Anzahl der Instanzen I dieser Klasse müssen nicht erst zur Laufzeit bekannt sein, aber was ich tun möchte, ist sicherzustellen, dass jede Variation von myArg zu einer anderen Singleton-Instanz führt (wenn also zweimal nach dem gleichen Wert gefragt wird, wird dieselbe Instanz zurückgegeben, aber unterschiedliche Argumente geben unterschiedliche Instanzen zurück).

Kennt jemand eine gute, vorzugsweise integrierte Methode, dies zu tun?

Ich fand einen Artikel, der für eine ältere Version von Ninject How To Ensure One Instance per Variation of Activation Parameters geschrieben wurde, aber hoffte, dass es eine sauberere Lösung für die neuere Version geben würde.

bearbeiten

Hier ist, was mit ich ging, von Akims Antwort unten angepasst:

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(); 
} 
+0

Check [ 'ninject.extensions.factory'] (https://github.com/ninject/ninject.extensions.factory) für automatische Implementierung der abstrakten Fabrik – Akim

Antwort

3

Sie Verhalten erreichen könnten erfordern von benutzerdefinierten Bereich Bereitstellung IBindingNamedWithOrOnSyntax<T> InScope(Func<IContext, object> scope) Verfahren für MyClass Bindung

mit

Zeigt an, dass über die Bindung aktivierte Instanzen re-u sein sollten sed solange das vom angegebenen Callback zurückgegebene Objekt am Leben bleibt (das heißt, wurde nicht Müll gesammelt).

So müssen Sie Wert des ersten Konstruktorargument zurückzukehren, von Func<IContext, object> scope und stellen Sie sicher, dass wäre es nicht sammeln. Hier

ist ein Ausschnitt:

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

+0

Wirklich mag, wie einfach diese Lösung ist, und ich habe ein bisschen Extrahilfe auf dem Wiki bei https://github.com/ninject/ninject/wiki/Object-Scopes. Die Instanzen müssen jedoch von einer Fabrik kommen, weißt du, ob dies mit der Factory-Erweiterung kompatibel ist? Ich nehme an, es wird sein. –

+0

Sicher funktioniert es! Siehe [aktualisiertes Beispiel mit 'ninject.extensions.faccotry'] (https://gist.github.com/4622557) – Akim

+0

Ich habe Ihr Beispiel ein wenig in meinem Code aufgeräumt, damit ich sagen konnte, welcher Parameter statt immer zu verwenden ist mit dem ersten und ich schrieb ein paar Tests. Funktioniert perfekt, danke !! –

Verwandte Themen