依赖注入和命名记录器

发布于 2024-09-13 08:55:56 字数 4407 浏览 5 评论 0原文

我有兴趣了解更多关于人们如何使用依赖注入平台注入日志记录的信息。尽管下面的链接和我的示例引用了 log4net 和 Unity,但我不一定会使用其中任何一个。对于依赖注入/IOC,我可能会使用 MEF,因为这是项目(大型)其余部分所采用的标准。

我对依赖注入/ioc 很陌生,对 C# 和 .NET 也很陌生(在过去 10 年左右的 VC6 和 VB6 之后,我在 C#/.NET 中编写了很少的生产代码)。我对现有的各种日志记录解决方案进行了大量调查,因此我认为我对它们的功能集有很好的掌握。我只是不太熟悉注入一个依赖项的实际机制(或者,也许更“正确”,获取注入一个依赖项的抽象版本)。

我看过其他与日志记录和/或依赖项注入相关的帖子,例如: 依赖注入和日志记录接口

记录最佳实践

Log4Net 包装类会是什么样子?

再次了解 log4net和 Unity IOC 配置

我的问题与“如何使用 ioc 工具 yyy 注入日志平台 xxx?”没有具体关系。相反,我感兴趣的是人们如何处理包装日志平台(通常但并不总是推荐)和配置(即 app.config)。例如,以 log4net 为例,我可以配置(在 app.config 中)多个记录器,然后以使用如下代码的标准方式获取这些记录器(无需依赖注入):

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

或者,如果我的记录器未命名对于一个类,而是对于一个功能区域,我可以这样做:

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

所以,我猜我的“要求”会是这样的:

  1. 我想将我的产品的源代码与对日志记录的直接依赖隔离开来平台。

  2. 希望能够通过某种依赖注入(可能是 MEF)直接或间接解析特定的命名记录器实例(可能在同一命名实例的所有请求者之间共享同一实例)。

  3. 我不知道我是否会将此称为硬要求,但我希望能够按需获取命名记录器(与类记录器不同)。例如,我可能会根据类名称为我的类创建一个记录器,但一种方法需要特别繁重的诊断,我想单独控制这些诊断。换句话说,我可能希望一个类“依赖”两个单独的记录器实例。

让我们从第一点开始。我读过很多文章,主要是在 stackoverflow 上,关于换行是否是一个好主意。请参阅上面的“最佳实践”链接,然后转到 jeffrey hantin 的评论,了解有关其为何不好的一种观点包装 log4net。如果你确实进行了包装(并且如果你可以有效地包装),你会严格包装以注入/删除直接依赖吗?或者您是否还会尝试抽象出部分或全部 log4net app.config 信息?

假设我想使用 System.Diagnostics,我可能想实现一个基于接口的记录器(甚至可能使用“通用”ILogger/ILog 接口),可能基于 TraceSource,以便我可以注入它。您是否会通过 TraceSource 实现该接口,并按原样使用 System.Diagnostics app.config 信息?

像这样的东西:

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

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

并像这样使用它:

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

继续第 2 个... 如何解析特定的命名记录器实例?我是否应该只利用我选择的日志平台的 app.config 信息(即根据 app.config 中的命名方案解析记录器)?那么,在 log4net 的情况下,我是否更愿意“注入”LogManager(请注意,我知道这是不可能的,因为它是静态对象)?我可以包装 LogManager(将其称为 MyLogManager),给它一个 ILogManager 接口,然后解析 MyLogManager.ILogManager 接口。我的其他对象可能对 ILogManager 有依赖关系(用 MEF 的说法是导入)(从实现它的程序集中导出)。现在我可以拥有这样的对象:

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

任何时候调用 ILogManager 时,它都会直接委托给 log4net 的 LogManager。或者,包装的 LogManager 是否可以获取基于 app.config 获取的 ILogger 实例,并按名称将它们添加到(?)MEF 容器中。稍后,当请求同名的记录器时,将查询包装的 LogManager 以获取该名称。如果有ILogger,就这样解决。如果 MEF 可以做到这一点,这样做有什么好处吗?

在这种情况下,实际上,只有 ILogManager 被“注入”,并且它可以按照 log4net 通常的方式分发 ILogger 实例。这种类型的注入(本质上是工厂)与注入命名记录器实例相比如何?这确实允许更轻松地利用 log4net(或其他日志平台)的 app.config 文件。

我知道我可以像这样从 MEF 容器中获取命名实例:

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

但是如何将命名实例放入容器中?我知道基于属性的模型,在该模型中我可以有不同的 ILogger 实现,每个实现都被命名(通过 MEF 属性),但这并没有真正帮助我。有没有一种方法可以创建类似 app.config (或其中的一部分)的内容,该内容可以按名称列出记录器(所有相同的实现)并且 MEF 可以读取?是否可以/应该有一个中央“管理器”(如 MyLogManager)通过底层 app.config 解析命名记录器,然后将解析后的记录器插入 MEF 容器?这样,有权访问同一 MEF 容器的其他人就可以使用它(尽管如果 MyLogManager 不知道如何使用 log4net 的 app.config 信息,容器似乎无法直接解析任何指定的记录器)。

这件事已经过去很久了。我希望它是连贯的。请随意分享有关如何将日志记录平台(我们最有可能考虑 log4net、NLog 或基于 System.Diagnostics 构建的其他东西(希望是薄的))依赖注入到您的应用程序中的任何具体信息。

您是否注入了“管理器”并让它返回记录器实例?

您是否在自己的配置部分或 DI 平台的配置部分中添加了一些自己的配置信息,以便更容易/可能直接注入记录器实例(即使您的依赖项位于 ILogger 而不是 ILogManager)。

拥有一个静态或全局容器,其中包含 ILogManager 接口或一组命名的 ILogger 实例怎么样?因此,日志依赖项不是传统意义上的注入(通过构造函数、属性或成员数据),而是根据需要显式解决。这是依赖注入的好还是坏方法?

我将其标记为社区维基,因为它看起来不像是一个有明确答案的问题。如果有人有其他感觉,请随意更改。

感谢您的帮助!

I am interested in learning more about how people inject logging with dependency injection platforms. Although the links below and my examples refer to log4net and Unity, I am not necessarily going to use either of those. For dependency injection/IOC, I will probably use MEF as that is the standard that the rest of the project (large) is settling on.

I am very new to dependency injection/ioc and am pretty new to C# and .NET (have written very little production code in C#/.NET after the past 10 years or so of VC6 and VB6). I have done a lot of investigation into the various logging solutions that are out there, so I think that I have a decent handle on their feature sets. I am just not familiar enough the with actual mechanics of getting one dependency injected (or, maybe more "correctly", getting an abstracted version of one dependency injected).

I have seen other posts related to logging and/or dependency injection like:
dependency injection and logging interfaces

Logging best practices

What would a Log4Net Wrapper class look like?

again about log4net and Unity IOC config

My question does not have specifically to do with "How to I inject logging platform xxx using ioc tool yyy?" Rather, I am interested in how people have handled wrapping the logging platform (as is often, but not always recommended) and configuration (i.e. app.config). For example, using log4net as an example, I could configure (in app.config) a number of loggers and then get those loggers (without dependency injection) in the standard way of using code like this:

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

Alternatively, if my logger is not named for a class, but rather, for a functional area, I could do this:

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

So, I guess that my "requirements" would be something like this:

  1. I would like to insulate my product's source from a direct dependency on a logging platform.

  2. I would like to be able to resolve a specific named logger instance (probably sharing the same instance among all requesters of the same named instance) either directly or indirectly by some kind of dependency injection, probably MEF.

  3. I don't know if I would call this a hard requirement, but I would like the ability to get a named logger (different than the class logger) on demand. For example, I might create a logger for my class based on the class name, but one method needs particulary heavy diagnostics that I would like to control separately. In other words, I might want a single class to "depend" on two separate logger instances.

Let's start with number 1. I have read a number of articles, primarily here on stackoverflow, about whether or not it is a good idea to wrap. See the "best practices" link above and go to jeffrey hantin's comment for one view about why it is bad to wrap log4net. If you did wrap (and if you could wrap effectively) would you wrap strictly for the purpose of injection/removal of direct depdency? Or would you also try to abstract away some or all of the log4net app.config information?

Let's say I want to use System.Diagnostics, I would probably want to implement an interface-based logger (maybe even using the "common" ILogger/ILog interface), probably based on TraceSource, so that I could inject it. Would you implement the interface, say over TraceSource, and just use the System.Diagnostics app.config information as is?

Something like this:

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

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

And use it like this:

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

Moving on to number 2 ... How to resolve a particular named logger instance? Should I just leverage the app.config information of the logging platform that I choose (i.e. resolve the loggers based on the naming scheme in the app.config)? So, in the case of log4net, might I prefer to "inject" LogManager (note that I know this is not possible since it is a static object)? I could wrap LogManager (call it MyLogManager), give it an ILogManager interface, and then resolve MyLogManager.ILogManager interface. My other objects could have a depenency (Import in MEF parlance) on ILogManager (Export from the assembly where it is implemented). Now I could have objects like this:

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

Any time ILogManager is called, it would directly delegate to log4net's LogManager. Alternatively, could the wrapped LogManager take the ILogger instances that it gets based on the app.config and add them to the(a ?) MEF container by name. Later, when a logger of the same name is requested, the wrapped LogManager is queried for that name. If the ILogger is there, it is resolved that way. If this is possible with MEF, is there any benefit do doing so?

In this case, really, only ILogManager is "injected" and it can hand out ILogger instances in the way that log4net normally does. How does this type of injection (essentially of a factory) compare to injecting the named logger instances? This does allow for more easy leveraging of log4net's (or other logging platform) app.config file.

I know that I can get named instances out of the MEF container like this:

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

But how do I get the named instances into the container? I know about the attribute based model where I could have different implementations of ILogger, each of which is named (via a MEF attribute), but that doesn't really help me. Is there a way to create something like an app.config (or a section therein) that would list the loggers (all of the same implementation) by name and that MEF could read? Could/should there be a central "manager" (like MyLogManager) that resolves named loggers via the underlying app.config and then inserts the resolved logger into the MEF container? This way it would be available to someone else that has access to the same MEF container (although without the MyLogManager's knowledge of how to use log4net's app.config information, it seems that the container would be unable to resolve any named loggers directly).

This has already gotten pretty long. I hope it that it is coherent. Please feel free to share any specific information about how you dependency injected a logging platform (we are most likely considering log4net, NLog, or something (hopefully thin) built on System.Diagnostics) into your application.

Did you inject the "manager" and have it return logger instances?

Did you add some of your own config information in your own config section or in your DI platform's config section to make it easier/possible to inject logger instances directly (i.e. make your dependencies be on ILogger rather than ILogManager).

What about having a static or global container that has either the ILogManager interface in it or the set of named ILogger instances in it. So, rather than injecting in the conventional sense (via constructor, property, or member data), the logging dependency is explicitly resolved on demand. Is this a good or bad way to dependency inject.

I am marking this as a community wiki since it doesn't seem like a question with a definite answer. If anyone feels otherwise, feel free to change it.

Thanks for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

廻憶裏菂餘溫 2024-09-20 08:55:56

我使用 Ninject 来解析记录器实例的当前类名,如下所示:

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

NLog 实现的构造函数可能如下所示:

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

我猜,这种方法也应该适用于其他 IOC 容器。

I'm using Ninject to resolve the current class name for the logger instance like this:

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

The constructor of a NLog Implementation could look like this:

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

This approach should work with other IOC containers as well, I guess.

国粹 2024-09-20 08:55:56

还可以使用 Common.Logging 外观或 简单的日志外观

这两者都采用服务定位器样式模式来检索 ILogger。

坦率地说,日志记录是我认为自动注入几乎没有价值的依赖项之一。

我的大多数需要日志记录服务的类如下所示:

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

}

通过利用 Simple Logging Facade,我已将项目从 log4net 切换到 NLog,并且除了使用 NLog 的应用程序日志记录之外,我还添加了来自使用 log4net 的第三方库的日志记录。也就是说,门面为我们服务得很好。

一个难以避免的警告是特定于一个或另一个日志记录框架的功能的丢失,其中最常见的例子可能是自定义日志记录级别。

One can also use Common.Logging facade or the Simple Logging Facade.

Both of these employ a service locator style pattern to retrieve an ILogger.

Frankly, logging is one of those dependencies I see little to no value in automatically injecting.

Most of my classes that require logging services look like this:

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

}

By utilizing the Simple Logging Facade I have switched a project from log4net to NLog, and I have added logging from a third party library that used log4net in addition to my application's logging using NLog. That is to say, the facade has served us well.

One caveat that is difficult to avoid is the loss of features specific to one logging framework or another, perhaps the most frequent example of which is custom logging levels.

雨后彩虹 2024-09-20 08:55:56

当您想要注入的记录器提供了日志记录平台(例如 log4net 或 NLog)时,这对于任何试图弄清楚如何注入记录器依赖项的人都是有利的。我的问题是,当我知道特定 ILogger 的解析将取决于了解依赖于 ILogger 的类的类型时,我无法理解如何使一个类(例如 MyClass)依赖于 ILogger 类型接口(例如我的班级)。 DI/IoC平台/容器如何获得正确的ILogger?

好吧,我已经查看了 Castle 和 NInject 的源代码并了解了它们的工作原理。我还查看了 AutoFac 和 StructureMap。

Castle 和 NInject 都提供了日志记录的实现。两者都支持 log4net 和 NLog。 Castle 还支持 System.Diagnostics。在这两种情况下,当平台解析给定对象的依赖关系时(例如,当平台创建 MyClass 且 MyClass 依赖于 ILogger 时),它将依赖关系 (ILogger) 的创建委托给 ILogger“提供者”(解析器可能是一个更通用术语)。然后,ILogger 提供程序的实现负责实际实例化 ILogger 实例并将其传回,然后注入到依赖类(例如 MyClass)中。在这两种情况下,提供者/解析器都知道依赖类的类型(例如 MyClass)。因此,当创建 MyClass 并解析其依赖项时,ILogger“解析器”知道该类是 MyClass。在使用 Castle 或 NInject 提供的日志记录解决方案的情况下,这意味着日志记录解决方案(作为 log4net 或 NLog 的包装器实现)获取类型 (MyClass),因此它可以委托给 log4net.LogManager.GetLogger() 或NLog.LogManager.GetLogger()。 (不能 100% 确定 log4net 和 NLog 的语法,但你明白了)。

虽然 AutoFac 和 StructureMap 不提供日志记录功能(至少我可以通过查看看出),但它们似乎确实提供了实现自定义解析器的能力。因此,如果您想编写自己的日志抽象层,您也可以编写相应的自定义解析器。这样,当容器想要解析 ILogger 时,您的解析器将用于获取正确的 ILogger 并且它可以访问当前上下文(即当前满足什么对象的依赖关系 - 什么对象依赖于 ILogger)。获取对象的类型,然后您就可以将 ILogger 的创建委托给当前配置的日志记录平台(您可能已在接口后面抽象并为其编写了解析器)。

因此,我怀疑需要但之前没有完全掌握的几个关键点是:

  1. 最终 DI 容器必须是
    以某种方式意识到日志记录的内容
    平台来使用。通常这是
    通过指定“ILogger”来完成
    由“解析器”解决
    特定于日志平台
    (因此,Castle 有 log4net、NLog、
    和 System.Diagnostics“解析器”
    (除其他外))。规格
    使用哪个解析器可以完成
    通过配置文件或以编程方式。

  2. 解析器需要知道
    依赖关系的上下文
    (ILogger) 正在解决。那
    是,如果 MyClass 已创建并且
    它依赖于ILogger,那么
    当解析器试图
    创建正确的 ILogger,它(
    解析器)必须知道当前类型
    (我的班级)。这样,解析器
    可以使用底层日志记录
    实现(log4net、NLog 等)
    以获得正确的记录器。

这些要点对于那些 DI/IoC 用户来说可能是显而易见的,但我现在才刚刚接触它,所以我花了一段时间才理解它。

我还没有弄清楚的一件事是 MEF 如何或是否可以实现类似的功能。我是否可以拥有一个依赖于接口的对象,然后在 MEF 创建该对象后以及解析接口/依赖项时执行我的代码?因此,假设我有一个这样的类:

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

  public MyClass()
  {
  }

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

当 MEF 解析 MyClass 的导入时,我可以有一些自己的代码(通过属性、通过 ILogger 实现上的额外接口、其他地方???)执行并解析ILogger 导入基于以下事实:当前在上下文中的是 MyClass,并返回一个(可能)与为 YourClass 检索到的 ILogger 实例不同的 ILogger 实例?我是否实施某种 MEF 提供程序?

至此,我对MEF还是一无所知。

This is for the benefit of anyone that is trying to figure out how to inject a logger dependency when the logger that you want to inject is provided a logging platform such as log4net or NLog. My problem was that I could not understand how I could make a class (e.g. MyClass) dependent on an ILogger-type interface when I knew that the resolution of the specific ILogger would depend on knowing the type of the class that is dependent on ILogger (e.g. MyClass). How does the DI/IoC platform/container get the right ILogger?

Well, I have looked at the source for Castle and NInject and have seen how they work. I have also looked AutoFac and StructureMap.

Castle and NInject both provide an implementation of logging. Both support log4net and NLog. Castle also supports System.Diagnostics. In both cases, when the platform resolves the dependencies for a given object (e.g. when the platform is creating MyClass and MyClass depends on ILogger) it delegates the creation of the dependency (ILogger) to the ILogger "provider" (resolver might be a more common term). The implementation of the ILogger provider is then responsible for actually instantiating an instance of ILogger and handing it back out, to then be injected into the dependent class (e.g. MyClass). In both cases the provider/resolver knows the type of the dependent class (e.g. MyClass). So, when MyClass has been created and its dependencies are being resolved, the ILogger "resolver" knows that the class is MyClass. In the case of using the Castle or NInject provided logging solutions, that means that the logging solution (implemented as a wrapper over log4net or NLog) gets the type (MyClass), so it can delegate down to log4net.LogManager.GetLogger() or NLog.LogManager.GetLogger(). (Not 100% sure of syntax for log4net and NLog, but you get the idea).

While AutoFac and StructureMap do not provide a logging facility (at least that I could tell by looking), they do seem to provide the ability to implement custom resolvers. So, if you wanted to write your own logging abstraction layer, you could also write a corresponding custom resolver. That way, when the container wants to resolve ILogger, your resolver would be used to get the correct ILogger AND it would have access to the current context (i.e. what object's dependencies are currently being satisfied - what object is dependent on ILogger). Get the type of the object, and you are ready to delegate the creation of the ILogger to the currently configured logging platform (that you have probably abstracted behind an interface and for which you have written a resolver).

So, a couple of key points which I suspected were required but that I did not fully grasp before are:

  1. Ultimately the DI container must be
    aware, somehow, of what logging
    platform to use. Typically this is
    done by specifying that "ILogger" is
    to be resolved by a "resolver" that
    is specific to a logging platform
    (hence, Castle has log4net, NLog,
    and System.Diagnostics "resolvers"
    (among others)). The specification
    of which resolver to use can be done
    via config file or programmatically.

  2. The resolver needs to know the
    context for which the dependency
    (ILogger) is being resolved. That
    is, if MyClass has been created and
    it is dependent on ILogger, then
    when the resolver is trying to
    create the correct ILogger, it (the
    resolver) must know the current type
    (MyClass). That way, the resolver
    can use the underlying logging
    implementation (log4net, NLog, etc)
    to get the correct logger.

These points might be obvious to those DI/IoC users out there, but I am just now coming into it, so it has taken me a while to get my head around it.

One thing that I have not figured out yet is how or if something like this is possible with MEF. Can I have an object dependent on an interface and then have my code execute after MEF has created the object and while the interface/dependency is being resolved? So, assume I have a class like this:

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

  public MyClass()
  {
  }

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

When MEF is resolving the imports for MyClass, can I have some of my own code (via an attribute, via an extra interface on the implementation of ILogger, elsewhere???) execute and resolve the ILogger import based on the fact that it is MyClass that is currently in context and give back a (potentially) different ILogger instance than would be retrieved for YourClass? Do I implement some sort of MEF Provider?

At this point, I still don't know about MEF.

凑诗 2024-09-20 08:55:56

我看到你已经找到了自己的答案:)但是,对于将来有关于如何不将自己绑定到特定日志框架的问题的人们,这个库: Common.Logging 有助于解决这种情况。

I see you figured out your own answer :) But, for folks in the future that have this question about how to NOT tie yourself to a particular logging framework, this library: Common.Logging helps with exactly that scenario.

错々过的事 2024-09-20 08:55:56

我制作了自定义 ServiceExportProvider,由我注册 Log4Net 记录器以通过 MEF 进行依赖注入的提供商。因此,您可以使用记录器进行不同类型的注入。

注入示例:

[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; }
}

使用示例:

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");
    }
}

控制台结果:

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 实现:

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));
        }
    }
}

I made my custom ServiceExportProvider, by the provider I register Log4Net logger for dependency injection by MEF. As result you can use logger for different kinds of injections.

Example of injections:

[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; }
}

Example of usage:

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");
    }
}

Result 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 implementation:

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));
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文