Domanda

Sono interessato a saperne di più su come le persone Iniettare la registrazione con piattaforme di iniezione di dipendenza. Anche se i link qui sotto ei miei esempi si riferiscono a log4net e Unity, non sto necessariamente intenzione di usare uno di questi. Per l'iniezione di dipendenza / CIO, io probabilmente utilizzare MEF come quello è lo standard che il resto del progetto (grande) si sta assestando su.

Sono molto nuovo per l'iniezione di dipendenza / CIO e sono abbastanza nuovo per C # e .NET (hanno scritto pochissimo codice di produzione in C # /. NET dopo gli ultimi 10 anni o giù di lì di VC6 e VB6). Ho fatto un sacco di indagine sulle varie soluzioni di registrazione che sono là fuori, quindi penso che ho un manico decente sul loro set di funzionalità. Io sono semplicemente non abbastanza familiarità con la meccanica della effettiva di ottenere una dipendenza iniettato (o, forse più "correttamente", ottenendo una versione astratta di una dipendenza iniettato).

ho visto altri post relative alla registrazione e / o l'iniezione di dipendenza simile: e la registrazione interfacce

Logging migliori pratiche

Cosa sarebbe una classe wrapper Log4Net assomigliare?

di nuovo di log4net e Unity CIO config

La mia domanda non ha specificamente a che fare con "Come ho iniettare piattaforma di registrazione xxx yyy utilizzando lo strumento del CIO?" Piuttosto, io sono interessato a come le persone hanno gestito avvolgendo la piattaforma di registrazione (come spesso, ma non sempre consigliato) e la configurazione (cioè app.config). Ad esempio, utilizzando log4net come esempio, ho potuto configurare (in app.config) un certo numero di logger e quindi ottenere quei forestale (senza iniezione di dipendenza) nel modo standard di utilizzo di codice come questo:

private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

In alternativa, se il mio registratore non è nominato per una classe, ma piuttosto, per un'area funzionale, ho potuto fare questo:

private static readonly ILog logger = LogManager.GetLogger("Login");
private static readonly ILog logger = LogManager.GetLogger("Query");
private static readonly ILog logger = LogManager.GetLogger("Report");

Quindi, credo che i miei "requisiti" sarebbe qualcosa di simile a questo:

  1. Vorrei per isolare la fonte di mio prodotto da una dipendenza diretta su una piattaforma di registrazione.

  2. Mi piacerebbe essere in grado di risolvere una specifica istanza denominata logger (probabilmente condividono la stessa istanza tra tutti i richiedenti di una stessa istanza denominata), direttamente o indirettamente, da una sorta di iniezione di dipendenza, probabilmente MEF.

  3. Non so se io definirei questo un requisito difficile, ma vorrei che la possibilità di ottenere un logger di nome (diverso dal logger classe) su richiesta. Ad esempio, potrei creare un logger per la mia classe in base al nome della classe, ma ha bisogno di un metodo particulary diagnostica pesanti che vorrei per controllare separatamente. In altre parole, potrei voler una singola classe a "dipendere" da due istanze logger separati.

La partenza di Let con il numero 1. Ho letto una serie di articoli, soprattutto qui in StackOverflow, circa se o non è una buona idea per avvolgere. Vedi il link "best practice" di cui sopra e andare a Jeffrey hantin 's commento per un punto di vista sul perché è male per avvolgere log4net. Se avete fatto involucro (e se si potrebbe avvolgere in modo efficace) vuoi avvolgere strettamente allo scopo di iniezione / rimozione di depdency diretta? Oppure volete provare anche ad astrarre alcune o tutte le informazioni log4net app.config?

dire Let voglio usare System.Diagnostics, probabilmente desidera implementare un logger un'interfaccia basata su (forse anche con il "comune" ILogger / ILOG interfaccia), probabilmente sulla base di TraceSource, in modo da poter iniettare. Volete implementare l'interfaccia, diciamo sopra TraceSource, e basta usare il sistema.Diagnostica App.config informazioni come lo è?

Qualcosa di simile a questo:

public class MyLogger : ILogger
{
  private TraceSource ts;
  public MyLogger(string name)
  {
    ts = new TraceSource(name);
  }

  public void ILogger.Log(string msg)
  {
    ts.TraceEvent(msg);
  }
}

E usare in questo modo:

private static readonly ILogger logger = new MyLogger("stackoverflow");
logger.Info("Hello world!")

Passando al numero 2 ... Come risolvere una particolare istanza logger di nome? Devo solo sfruttare le informazioni app.config della piattaforma di registrazione che ho scelto (vale a dire risolvere i taglialegna in base allo schema di denominazione nel app.config)? Così, nel caso di log4net, potrei preferire a "iniettare" LogManager (nota che so che questo non è possibile in quanto si tratta di un oggetto statico)? Potrei avvolgere LogManager (lo chiamano MyLogManager), dargli un'interfaccia ILogManager, e quindi l'interfaccia determinazione MyLogManager.ILogManager. I miei altri oggetti potrebbero avere un depenency (Importa in gergo MEF) su ILogManager (Export dal gruppo in cui è implementato). Ora potrei avere oggetti come questo:

public class MyClass
{
  private ILogger logger;
  public MyClass([Import(typeof(ILogManager))] logManager)
  {
    logger = logManager.GetLogger("MyClass");
  }
}

Ogni volta ILogManager si chiama, sarebbe direttamente delegare al di log4net LogManager. In alternativa, potrebbe il LogManager avvolto prendere le istanze ILogger che venga basata su app.config e aggiungerli alla (un?) Container MEF per nome. Più tardi, quando viene richiesto un registratore con lo stesso nome, il LogManager avvolto viene interrogato per quel nome. Se l'ILogger è lì, si è risolto in questo modo. Se questo è possibile con MEF, c'è qualche vantaggio non farlo?

In questo caso, in realtà, solo ILogManager viene "iniettato" e può distribuire istanze ILogger nel modo in cui log4net fa normalmente. Come funziona questo tipo di iniezione (essenzialmente di una fabbrica) confronta con l'iniezione le istanze denominate logger? Ciò permetterebbe una maggiore facilità di leva (o altra piattaforma di registrazione) di log4net file app.config.

So che posso ottenere le istanze denominate fuori dal contenitore MEF in questo modo:

var container = new CompositionContainer(<catalogs and other stuff>);
ILogger logger = container.GetExportedValue<ILogger>("ThisLogger");

Ma come faccio ad avere le istanze denominate nel contenitore? So del modello basato attributo in cui ho potuto avere diverse implementazioni di ILogger, ognuno dei quali prende il nome (tramite un attributo MEF), ma che in realtà non mi aiuti. C'è un modo per creare qualcosa di simile a un app.config (o di una sezione di essa), che elenca le forestale (tutti della stessa applicazione) per nome e che MEF sapeva leggere? Potrebbe / dovrebbe esserci un "manager" centrale (come MyLogManager) che risolve chiamato logger tramite l'app.config sottostante e quindi inserisce il registratore risolto nel contenitore MEF? In questo modo sarebbe stato disponibile a qualcun altro che ha accesso allo stesso contenitore MEF (anche se senza la conoscenza del MyLogManager di come utilizzare le informazioni app.config di log4net, sembra che il contenitore non sarebbe in grado di risolvere direttamente eventuali taglialegna di nome).

Questo ha già ottenuto piuttosto lunga. Spero che sia coerente. Non esitate a condividere tutte le informazioni specifiche sulle modalità di dipendenza iniettato una piattaforma di registrazione (stiamo valutando molto probabilmente log4net, NLog, o qualcosa del genere (si spera sottile) costruito su System.Diagnostics) nella vostra applicazione.

Ti è iniettare il "manager" e lo hanno tornare istanze logger?

Lo si aggiungono alcune delle proprie informazioni di configurazione nella propria sezione di configurazione o nella sezione di configurazione della vostra piattaforma DI per rendere più facile / possibile iniettare direttamente le istanze logger (vale a dire rendere le dipendenze essere sulla ILogger piuttosto che ILogManager).

Che dire di avere uno statico o contenitore globale che ha l'interfaccia ILogManager in esso o l'insieme di istanze ILogger denominate in esso. Così, invece di iniettare nel senso convenzionale (via costruttore, la proprietà o dati membro), la dipendenza di registrazione è esplicitamente risolto su richiesta. E 'questo un bene o un brutto modo di dipendenza iniettare.

sto segnando questo come un wiki comunità dal momento che non sembra una domanda con una risposta definitiva. Se qualcuno si sente in caso contrario, si sentono liberi di cambiarlo.

Grazie per qualsiasi aiuto!

È stato utile?

Soluzione 3

Questo è per il beneficio di tutti coloro che sta cercando di capire come iniettare una dipendenza registratore quando il registratore che si desidera iniettare è prevista una piattaforma di registrazione come log4net o NLog. Il mio problema era che non riuscivo a capire come avrei potuto fare una classe (ad esempio MyClass) dipendente da un'interfaccia ILogger tipo quando ho saputo che la risoluzione dello specifico ILogger dipenderebbe conoscere il tipo della classe che dipende da ILogger ( es MyClass). Come funziona il DI / CIO piattaforma / contenitore di ottenere l'ILogger giusto?

Bene, ho guardato la fonte di Castello e Ninject e hanno visto come funzionano. Ho anche guardato autofac e StructureMap.

Castello e Ninject entrambi forniscono un'implementazione di registrazione. Sia log4net sostegno e NLog. Castello supporta anche System.Diagnostics. In entrambi i casi, quando la piattaforma risolve le dipendenze per un dato oggetto (ad esempio quando la piattaforma è la creazione MyClass e MyClass dipende ILogger) delega la creazione della dipendenza (ILogger) per il "fornitore" ILogger (resolver potrebbe essere un più termine comune). L'attuazione del provider ILogger è quindi responsabile per effettivamente istanziare un'istanza di ILogger e restituirla fuori, per poi essere iniettato nella classe dipendente (ad esempio MyClass). In entrambi i casi il fornitore / resolver conosce il tipo della classe dipendente (ad esempio MyClass). Così, quando MyClass è stato creato e le sue dipendenze vengono risolte, il "risolutore" ILogger sa che la classe è MyClass. Nel caso di utilizzo Castello o Ninject fornito registrazione soluzioni, che significa che la soluzione di registrazione (implementato come un involucro sopra log4net o NLog) ottiene il tipo (MyClass), quindi può delegare verso log4net.LogManager.GetLogger () o NLog.LogManager.GetLogger (). (Non al 100% sicuro della sintassi per log4net e NLog, ma si ottiene l'idea).

Mentre autofac e StructureMap non forniscono una funzione di registrazione (almeno che io potessi dire guardando), che sembrano offrire la possibilità di implementare resolver personalizzati. Quindi, se si voleva scrivere il proprio livello di registrazione di astrazione, si potrebbe anche scrivere un corrispondente resolver personalizzato. In questo modo, quando il contenitore vuole risolvere ILogger, il vostro resolver sarebbero stati utilizzati per ottenere il corretto ILogger e sarebbe avere accesso al contesto attuale (vale a dire ciò che le dipendenze dell'oggetto sono attualmente soddisfatti - quale oggetto dipende ILogger). Prendi il tipo di oggetto, e si è pronti a delegare la creazione del ILogger alla piattaforma di registrazione attualmente configurato (che probabilmente avete astratta dietro un'interfaccia e per la quale hai scritto un resolver).

Così, un paio di punti chiave che ho sospettato è stato richiesto, ma che non ho cogliere appieno prima sono:

  1. In definitiva il contenitore DI deve essere consapevoli, in qualche modo, di ciò che la registrazione la piattaforma da utilizzare. In genere si tratta fatto specificando che "ILogger" è essere risolti da un "risolutore" che è specifico per una piattaforma di registrazione (Da qui, il Castello ha log4net, NLog, e System.Diagnostics "risolutori" (tra gli altri)). la specifica dei quali resolver ad uso può essere fatto tramite file di configurazione o di programmazione.

  2. Il resolver deve conoscere il     contesto per cui la dipendenza     (ILogger) è in via di risoluzione. Quella     è, se MyClass è stato creato e     è dipendente ILogger, allora     quando il resolver sta cercando di     creare la giusta ILogger, esso (il     resolver) deve conoscere il tipo di corrente     (La mia classe). In questo modo, il risolutore     può usare la registrazione sottostante     implementazione (log4net, NLog, ecc)     per ottenere il logger corretto.

Questi punti potrebbe essere ovvio per quegli utenti DI / IoC là fuori, ma sono solo ora entrando in esso, in modo che mi ha preso un po 'per ottenere la mia testa intorno ad esso.

Una cosa che non ho ancora capito è come o se sia possibile una cosa del genere con MEF.Posso avere un oggetto dipendente da un'interfaccia e poi ho il mio codice eseguito dopo MEF ha creato l'oggetto e mentre l'interfaccia / dipendenza è stato risolto? Quindi, assumere ho una classe come questa:

public class MyClass
{
  [Import(ILogger)]
  public ILogger logger;

  public MyClass()
  {
  }

  public void DoSomething()
  {
    logger.Info("Hello World!");
  }
}

Quando MEF sta risolvendo le importazioni per MyClass, posso avere un po 'del mio proprio codice (tramite un attributo, tramite un'interfaccia in più sull'attuazione della ILogger, altrove ???) eseguire e risolvere l'importazione ILogger basata sul fatto che è MyClass che è attualmente nel contesto e restituire una (potenzialmente) istanza ILogger diverso sarebbe recuperato per YourClass? Faccio a implementare una sorta di MEF Provider?

A questo punto, io ancora non conoscono MEF.

Altri suggerimenti

sto usando Ninject per risolvere il nome della classe corrente per l'istanza logger in questo modo:

kernel.Bind<ILogger>().To<NLogLogger>()
  .WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName);

Il costruttore di un'implementazione NLog potrebbe assomigliare a questo:

public NLogLogger(string currentClassName)
{
  _logger = LogManager.GetLogger(currentClassName);
}

Questo approccio dovrebbe funzionare con altri contenitori CIO così, immagino.

Si può anche utilizzare Common.Logging facciata o il semplice registrazione Facciata .

Entrambi questi impiegano un modello di servizio di individuazione stile per recuperare un ILogger.

Francamente, la registrazione è una di quelle dipendenze vedo poco o nessun valore nel iniettando automaticamente.

La maggior parte delle mie classi che richiedono servizi di registrazione simile a questa:

public class MyClassThatLogs {
    private readonly ILogger log = Slf.LoggerService.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);

}

Utilizzando la registrazione semplice facciata ho acceso un progetto da log4net a NLog, e ho aggiunto la registrazione da una libreria di terze parti che log4net utilizzato in aggiunta alla registrazione di mia applicazione usando NLog. Vale a dire, la facciata ci ha servito bene.

Un avvertimento che è difficile da evitare è la perdita di funzionalità specifiche per un quadro di registrazione o di un altro, forse l'esempio più frequente dei quali è livelli di registrazione personalizzati.

Ti vedo capito la tua risposta proprio :) Ma, per la gente, in futuro, che hanno questa domanda su come NON legarsi a un particolare quadro di registrazione, questa biblioteca: Common.Logging aiuta con esattamente questo scenario.

Ho fatto il mio personalizzato ServiceExportProvider , dal fornitore mi iscrivo Log4Net logger per l'iniezione di dipendenza dal MEF. Come risultato si può usare logger per diversi tipi di iniezioni.

Esempio di iniezioni:

[Export]
public class Part
{
    [ImportingConstructor]
    public Part(ILog log)
    {
        Log = log;
    }

    public ILog Log { get; }
}

[Export(typeof(AnotherPart))]
public class AnotherPart
{
    [Import]
    public ILog Log { get; set; }
}

Esempio di utilizzo:

class Program
{
    static CompositionContainer CreateContainer()
    {
        var logFactoryProvider = new ServiceExportProvider<ILog>(LogManager.GetLogger);
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        return new CompositionContainer(catalog, logFactoryProvider);
    }

    static void Main(string[] args)
    {
        log4net.Config.XmlConfigurator.Configure();
        var container = CreateContainer();
        var part = container.GetExport<Part>().Value;
        part.Log.Info("Hello, world! - 1");
        var anotherPart = container.GetExport<AnotherPart>().Value;
        anotherPart.Log.Fatal("Hello, world! - 2");
    }
}

Risultato in console:

2016-11-21 13:55:16,152 INFO  Log4Mef.Part - Hello, world! - 1
2016-11-21 13:55:16,572 FATAL Log4Mef.AnotherPart - Hello, world! - 2

ServiceExportProvider applicazione:

public class ServiceExportProvider<TContract> : ExportProvider
{
    private readonly Func<string, TContract> _factoryMethod;

    public ServiceExportProvider(Func<string, TContract> factoryMethod)
    {
        _factoryMethod = factoryMethod;
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
    {
        var cb = definition as ContractBasedImportDefinition;
        if (cb?.RequiredTypeIdentity == typeof(TContract).FullName)
        {
            var ce = definition as ICompositionElement;
            var displayName = ce?.Origin?.DisplayName;
            yield return new Export(definition.ContractName, () => _factoryMethod(displayName));
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top