2013-01-24 3 views
5

che sto usando Ninject istanziare alcuni oggetti con un arg costruttore passato, ad esempio:istanze Ninject Memoize a Singleton Ambito

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

Il numero di istanze che ho bisogno di questa classe non sarà noto fino al runtime, ma quello che voglio fare è garantire che ogni variazione di myArg abbia come risultato un'istanza singleton diversa (quindi chiedere lo stesso valore due volte restituisce la stessa istanza, ma argomenti diversi restituiscono istanze diverse).

Qualcuno sa di un buon modo, preferibilmente integrato, per farlo?

Ho trovato un articolo scritto per una versione precedente di Ninject How To Ensure One Instance per Variation of Activation Parameters ma speravo che ci sarebbe stata una soluzione più pulita per la versione più recente.

Modifica

Ecco quello che ho passato con, adattato da risposta di Akim di seguito:

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

Controllare [ 'ninject.extensions.factory'] (https://github.com/ninject/ninject.extensions.factory) per implementazione automatica della fabbrica astratta – Akim

risposta

3

Si potrebbe ottenere richiedono un comportamento fornendo ambito personalizzato utilizzando IBindingNamedWithOrOnSyntax<T> InScope(Func<IContext, object> scope) metodo per MyClass vincolante

Indica che le istanze attivate tramite la rilegatura devono essere ri-u sed finché l'oggetto restituito dal callback fornito rimane attivo (ovvero, non è stato raccolto dati inutili).

Quindi, è necessario per restituire valore del primo argomento del costruttore da Func<IContext, object> scope e fare in modo che non avrebbe raccoglierlo.

Ecco un frammento:

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

Davvero come questa soluzione è semplice, e ho avuto un po 'di aiuto extra sulla wiki su https://github.com/ninject/ninject/wiki/Object-Scopes. Le istanze dovranno provenire da una fabbrica, sai se questo sarà compatibile con l'estensione di fabbrica? Presumo che lo sarà. –

+0

Sicuro funziona! Vedi [esempio aggiornato con 'ninject.extensions.facotry'] (https://gist.github.com/4622557) – Akim

+0

Ho riordinato un po 'il tuo esempio nel mio codice in modo che potessi dire quale parametro usare invece di sempre usando il primo e ho scritto alcuni test. Funziona perfettamente, grazie !! –