73 votes

Injection de dépendance et enregistreurs nommés

Je suis intéressé à en apprendre davantage sur la façon dont les gens injecter de l'enregistrement à l'aide d'injection de dépendance des plates-formes. Bien que les liens ci-dessous et mes exemples se réfèrent à log4net et de l'Unité, je ne suis pas forcément d'utiliser l'un de ceux-ci. Pour l'injection de dépendances/CIO, je vais probablement l'utiliser MEF comme c'est la norme que le reste du projet (grand) est en cours d'installation.

Je suis très nouveau à l'injection de dépendances/cio et je suis assez novice en C# et .NET (ont écrit très peu de production de code en C#/.NET, après les 10 dernières années de VC6 et VB6). J'ai fait beaucoup de recherches sur les différentes solutions de connexion qui sont là-bas, donc je pense que j'ai une bonne maîtrise de leurs ensembles de fonctionnalités. Je ne suis pas assez familier avec le réel de la mécanique de l'obtention d'une dépendance à l'injection (ou, peut-être plus "correctement", d'obtenir une version abstraite de l'une dépendance injecté).

J'ai vu d'autres messages liés à l'exploitation forestière et/ou de l'injection de dépendances comme: http://stackoverflow.com/questions/522370/dependency-injection-and-logging-interfaces

http://stackoverflow.com/questions/576185/logging-best-practices

http://stackoverflow.com/questions/166438/what-would-a-log4net-wrapper-class-look-like

http://stackoverflow.com/questions/1320844/again-about-log4net-and-unity-ioc-config

Ma question n'est pas spécialement à voir avec "Comment j'ai injecter de la plateforme d'enregistrement xxx à l'aide du cio outil yyy?" Je suis plutôt intéressé par la façon dont les gens ont géré emballage de la plateforme d'enregistrement (comme c'est souvent, mais pas toujours recommandé) et de configuration (c-app.config). Par exemple, à l'aide de log4net comme un exemple, j'ai pu configurer (dans l'app.config) un certain nombre de bûcherons, puis obtenir ces enregistreurs (sans injection de dépendance) dans le niveau moyen de l'aide d'un code comme ceci:

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

Sinon, si mon enregistreur n'est pas nommé pour une classe, mais plutôt, pour un domaine fonctionnel, je pourrais faire ceci:

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

Donc, je suppose que mes "exigences" serait quelque chose comme ceci:

  1. Je voudrais isoler mon produit est source d'une dépendance directe sur une plateforme d'enregistrement.

  2. Je voudrais être en mesure de résoudre un spécifique nommé journal instance (probablement partage la même instance parmi tous les demandeurs de la même instance nommée), soit directement ou indirectement, par une sorte de l'injection de dépendance, probablement MEF.

  3. Je ne sais pas si je dirais que c'est un dur, mais je voudrais la possibilité d'obtenir un nommé enregistreur (différent de la classe logger) sur demande. Par exemple, je pourrais créer un historique de ma classe basée sur le nom de la classe, mais une méthode particulièrement lourd diagnostics que je voudrais contrôler séparément. En d'autres termes, j'aimerais une classe unique de "dépendre" sur deux enregistreur de cas.

Nous allons commencer avec le numéro 1. J'ai lu un certain nombre d'articles, principalement ici sur stackoverflow, quant à savoir si ou non il est une bonne idée d'envelopper. Voir les "meilleures pratiques" du lien ci-dessus et accédez à jeffrey hantin'commentaire pour un point de vue sur pourquoi c'est mauvais pour l'envelopper de log4net. Si vous n'avez wrap (et si vous pouvez rassembler efficacement) pourriez-vous envelopper strictement dans le but d'injection et/ou la suppression directe de depdency? Ou souhaitez-vous également tenter de faire abstraction de certains ou de tous les log4net app.les informations de configuration?

Disons que je veux utiliser le Système.Diagnostics, j'aurais probablement envie de mettre en œuvre une interface basée sur l'enregistreur (peut-être même à l'aide de la "commune" de ILogger/ILog interface), probablement basée sur TraceSource, pour que je puisse injecter. Souhaitez-vous mettre en œuvre l'interface, dire plus TraceSource, et il suffit d'utiliser le Système.Diagnostics app.les informations de configuration que c'est?

Quelque chose comme ceci:

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

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

Et de l'utiliser comme ceci:

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

De passer à numéro 2 ... Comment faire pour résoudre un particulier nommé journal instance? Devrais-je tirer parti de l'application.les informations de configuration de la plateforme d'enregistrement de mon choix (c'est à dire résoudre les bûcherons basé sur le schéma de nommage dans l'application.config)? Ainsi, dans le cas de log4net, pourrais-je préfère à "injecter" LogManager (notez que je sais, c'est pas possible puisque c'est un objet statique)? J'ai pu envelopper LogManager (appeler MyLogManager), lui donner un ILogManager de l'interface, puis résoudre MyLogManager.ILogManager interface. Mes autres objets pourraient avoir un depenency (Import dans MEF jargon) sur ILogManager (Exportation de l'assemblée où il est mis en œuvre). Maintenant, je pourrais avoir des objets comme ceci:

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

Tout moment ILogManager est appelée, elle serait directement délégué à log4net est LogManager. Sinon, l'enveloppé LogManager prendre la ILogger instances qu'il se base sur l'application.config et les ajouter à la(une ?) Conteneur MEF par son nom. Plus tard, quand un enregistreur du même nom est demandé, la enveloppé LogManager est interrogé pour ce nom. Si le ILogger est là, il est résolu de cette façon. Si c'est possible avec le MEF, est-il un bénéfice de le faire?

Dans ce cas, en réalité, que ILogManager est "injecté" et il peut main ILogger cas dans la façon dont log4net fait normalement. Comment fonctionne ce type d'injection (essentiellement d'une usine) comparer à injecter le nom de l'enregistreur de cas? Cela ne permettra plus facilement en tirant parti de log4net (ou autre plateforme d'enregistrement) application.fichier de configuration.

Je sais que je peux obtenir des instances nommées de la MEF conteneur comme ceci:

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

Mais comment puis-je obtenir les instances nommées dans le conteneur? Je sais que sur les attributs de base du modèle où je pourrais avoir des implémentations différentes de ILogger, chaque de ce qui est nommé (par l'intermédiaire d'un MEF attribut), mais qui n'a pas vraiment m'aider. Est-il un moyen de créer quelque chose comme une application.config (ou une section de celle-ci) que la liste des enregistreurs (tous de la même mise en œuvre) par nom et que la MEF pu lire? Pourrait/devrait-il y avoir une centrale de "manager" (comme MyLogManager) qui résout nommé enregistreurs via l'application sous-jacente.config puis insère le résolu logger dans le conteneur MEF? De cette manière, il serait disponible pour quelqu'un d'autre qui a accès à la même conteneur MEF (bien que sans le MyLogManager de connaissances sur la façon d'utiliser log4net app.les informations de configuration, il semble que le conteneur serait incapable de résoudre un nom de bûcherons directement).

Cela a déjà reçu assez long. J'espère que c'est cohérent. N'hésitez pas à partager aucune information spécifique sur la façon dont vous la dépendance injecté une plateforme d'enregistrement (nous sommes le plus probable compte tenu de log4net, NLog, ou quelque chose (j'espère mince), construit sur le Système.Diagnostics) de votre application.

Avez-vous injecter le "gestionnaire" et le retour de l'enregistreur de cas?

Avez-vous d'ajouter certains de vos propres informations de configuration dans votre propre section de configuration ou dans votre DI plate-forme de la section de configuration pour le rendre plus facile/possible d'injecter de l'enregistreur des instances directement (c'est à dire faire vos dépendances sur ILogger plutôt que ILogManager).

Ce que le fait d'avoir une statique ou conteneur global qui dispose de la ILogManager de l'interface ou de l'ensemble d'nommé ILogger instances. Donc, plutôt que d'injecter dans le sens classique du terme (via le constructeur, de propriété, ou un membre de données), l'enregistrement de la dépendance est explicitement résolu à la demande. Est-ce une bonne ou une mauvaise façon de dépendance à l'injection.

Je suis ce marquage comme un wiki de la communauté, car il ne semble pas être une question avec une réponse définitive. Si quelqu'un se sent par ailleurs, n'hésitez pas à le changer.

Merci pour toute aide!

38voto

J'utilise Ninject pour résoudre le nom de classe actuel de l'instance de consignation, comme suit:

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

Le constructeur d'une implémentation NLog pourrait ressembler à ceci:

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

Je suppose que cette approche devrait également fonctionner avec d’autres conteneurs IOC.

16voto

qes Points 11681

On peut également utiliser des Communes.La journalisation de la façade ou la Journalisation Simple Façade.

Ces deux utilisent un service de localisateur de style de motif pour récupérer un ILogger.

Franchement, l'exploitation forestière est l'une de ces dépendances, je vois peu ou pas de valeur à injecter automatiquement.

La plupart de mes classes qui nécessitent de la journalisation des services ressembler à ceci:

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

}

En utilisant la Journalisation Simple Façade j'ai changé un projet de log4net à NLog, et j'ai ajouté de la journalisation d'un tiers bibliothèque utilisée log4net en plus de ma demande de journalisation à l'aide de NLog. C'est-à-dire, la façade nous a bien servi.

Une mise en garde qui est difficile à éviter, c'est de la perte de fonctionnalités spécifiques à l'un de journalisation ou d'une autre, peut-être l'exemple le plus fréquent de mesure des niveaux d'enregistrement.

13voto

wageoghe Points 14676

C'est pour le bénéfice de quelqu'un qui est en train d'essayer de comprendre comment injecter un enregistreur de dépendance lorsque l'enregistreur que vous voulez injecter est fourni une plateforme d'enregistrement tels que log4net ou NLog. Mon problème était que je ne pouvais pas comprendre comment j'ai pu faire une classe (par exemple, MyClass) dépend d'un ILogger-type d'interface quand j'ai su que la résolution du spécifique ILogger dépendra de connaître le type de la classe qui dépend de ILogger (par exemple MyClass). Comment le DI/Cio de plate-forme/conteneur obtenir le droit ILogger?

Eh bien, j'ai regardé le source pour le Château et NInject et ont vu comment ils fonctionnent. J'ai aussi regardé AutoFac et StructureMap.

Château et NInject à la fois de fournir une implémentation de l'enregistrement. Les deux charge log4net et NLog. Château prend également en charge le Système.Diagnostics. Dans les deux cas, lorsque la plate-forme résout les dépendances d'un objet donné (par exemple, lorsque la plate-forme est la création de MyClass et MyClass dépend ILogger) il délègue la création de la dépendance (ILogger) à la ILogger "fournisseur" (résolveur peut être un terme plus courant). La mise en œuvre de la ILogger fournisseur est responsable pour le fait de créer une instance de ILogger et de les remettre en arrière, pour ensuite être injecté dans la classe dépendante (par exemple, MyClass). Dans les deux cas, le fournisseur/résolveur sait le type de la classe dépendante (par exemple, MyClass). Donc, quand Maclasse a été créé et ses dépendances sont résolues, le ILogger "résolveur" sait que la classe Maclasse. Dans le cas de l'utilisation du Château ou NInject fourni solutions d'enregistrement, ce qui signifie que la solution d'enregistrement (mise en œuvre de l'enveloppe de plus de log4net ou NLog) obtient le type (Maclasse), de sorte qu'il peut déléguer vers le bas pour log4net.LogManager.GetLogger() ou NLog.LogManager.GetLogger(). (Pas sûr à 100% de la syntaxe pour log4net et NLog, mais vous voyez l'idée).

Alors que AutoFac et StructureMap ne pas fournir une fonction de journalisation (à moins que je puisse dire par la recherche), ils ne semblent offrir la capacité de mettre en œuvre des outils de résolution personnalisés. Donc, si vous voulez écrire votre propre journalisation de la couche d'abstraction, vous pouvez également écrire un correspondant résolveur personnalisé. De cette manière, lorsque le conteneur souhaite résoudre ILogger, votre résolveur serait utilisé pour obtenir la bonne ILogger ET il aurait accès au contexte actuel (c'est à dire que l'objet de dépendances sont actuellement satisfaits - ce que l'objet dépend de ILogger). Obtenir le type de l'objet, et vous êtes prêt à déléguer la création de la ILogger à la configuration actuelle de l'plateforme d'enregistrement (que vous avez probablement abstraite derrière une interface, et pour qui vous avez écrit un résolveur).

Ainsi, un couple de points clés qui je suspects ont été nécessaires, mais que je n'ai pas entièrement saisi avant sont:

  1. En fin de compte la DI conteneur doit être conscience, en quelque sorte, de quel enregistrement plate-forme à utiliser. Typiquement, cela est fait en spécifiant que "ILogger" est être résolu par une "résolution" qui est spécifique à une plateforme d'enregistrement (par conséquent, le Château a log4net, NLog, et le Système.Diagnostics "résolveurs" (parmi d'autres)). La spécification de qui de résolution à utiliser qui peut être fait via le fichier de config ou de la programmation.

  2. Le résolveur besoins de connaître l' contexte pour lequel la dépendance (ILogger) est en cours de résolution. Que est, si Maclasse a été créé et il est tributaire de la ILogger, puis lorsque le solveur tente de créer le bon ILogger, elle (la résolveur) doit connaître le type de courant (Maclasse). De cette façon, le résolveur pouvez utiliser la sous-jacentes de la journalisation la mise en œuvre (log4net, NLog, etc) pour obtenir le bon enregistreur.

Ces points pourraient être évident pour ceux DI/Coi d'utilisateurs là-bas, mais je viens de rentrer en lui, donc il m'a fallu un certain temps pour obtenir ma tête autour de lui.

Une chose que je n'ai pas compris encore est de savoir comment ou si quelque chose comme cela est possible avec le MEF. Puis-je avoir un objet dépend d'une interface, puis avoir mon code à exécuter après le MEF a créé l'objet, et alors que l'interface/la dépendance est en train d'être résolu? Ainsi, supposons que j'ai une classe comme ceci:

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

  public MyClass()
  {
  }

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

Lors de la MEF est de résoudre les importations de l ' MyClass, je peux avoir une partie de mon code (via un attribut, par l'intermédiaire d'une interface supplémentaire sur la mise en œuvre de ILogger, d'ailleurs???) exécuter et régler les ILogger importation basée sur le fait que c'est MyClass qui est actuellement en contexte et de donner en retour un (potentiellement) différents ILogger exemple que doit être récupérées pour YourClass? Dois-je mettre en œuvre une sorte de MEF Fournisseur?

À ce stade, je ne sais toujours pas sur MEF.

5voto

CubanX Points 3603

Je vois que vous avez compris votre propre réponse :) Mais pour les futurs utilisateurs de cette question sur la façon de ne PAS vous lier à un cadre de journalisation particulier, cette bibliothèque: Common.Logging aide exactement à ce scénario.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X