Unity / EntLib:将依赖项注入CustomTraceListener [英] Unity / EntLib: Injecting a dependency into a CustomTraceListener

查看:87
本文介绍了Unity / EntLib:将依赖项注入CustomTraceListener的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对不起,这是一个非常特殊的主题,因此许多人可能对此并不感兴趣。 :-(

Sorry, this is quite a special topic so this may not be of interest to many. :-(

但是,我需要做以下事情:

However, I need to do the following thing:


  • 我有一个应用程序,它提供对某种控制台窗口(这是WPF窗口)的日志记录,这是由于应用程序要求以及因为应用程序即使在这里也需要显得浮华-我们的特殊客户要求这样做,并且每次我们都在谈论它满足)

  • I have an application that provides logging to some kind of console window (it's a WPF window, because of application requirements and because the application needs to look flashy even here - our special customer asked for that and talks about it every time we meet)

提供线程不可知的日志记录,我创建了一个接口/实现对 IGUIController / GUIController

To provide thread - agnostic logging I created an interface / implementation pair "IGUIController" / "GUIController"

到目前为止,一切都很好。

So far, so good. It's all fine.

但是:


  • 我需要我自己的自定义跟踪侦听器(我称之为 GUITraceListener),该侦听器使用IGUIController-接口将特定的日志消息写入该华而不实的WPF​​-窗口

到目前为止,我的解决方案是在我的GUIController上拥有一个被黑的,老套的代码气味单例(样式为当前)(是,我很as愧,我确实知道这太可怕了e)我这样调用:

So far, my solution is to have a hacked, old skool code smell singleton - style "Current" - property on my GUIController (yes, and I'm ashamed, and I DO know that this is terrible) which I invoke like this:

GUIController.Current.Log(message);

很明显,这是不行的。

因此,正确的方法是注入依赖项(当然)。但是,当我这样做时,我收到了抱怨(运行时),因为我没有为我的GUITraceListener-类提供构造函数带有0(零)个参数。

So, the correct way to do that would be to inject the dependency (of course). However, when I do that, I get the complaint (runtime), that I don't provide a constructor with 0 (zero) arguments for my GUITraceListener - class.

事实上,我在这里得到了

As a matter of fact, I get that here:

EnterpriseLibraryContainer.ConfigureContainer(configurator, 
ConfigurationSourceFactory.Create());

投诉是:

ArgumentException未处理
无法为GUITraceListener找到合适的0参数构造函数

ArgumentException was unhandled Unable to find appropriate 0 argument constructor for GUITraceListener

这确实很糟糕。我的意思是,Unity是企业库的一部分,为什么这些人不只是增加注入我的依赖项的可能性?

This is really bad. I mean, Unity is part of the Enterprise Library, why didn't those guys simply add the possibility of injecting my dependencies?

澄清:

我最后想要得到的是:

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="guiController"></param>
    public GUITraceListener(IGUIController guiController)
    {
        // Store the dependencies
        this.m_GUIController = guiController;
    }


推荐答案

Entlib 5 可以执行此操作。我猜您在侦听器类上有一个[ConfigurationElementType(typeof(CustomTraceListenerData)),对吧?

Entlib 5 does have the ability to do this. I'm guessing you've got a [ConfigurationElementType(typeof(CustomTraceListenerData)] on your listener class, right?

Entlib 5设计为独立于容器。 ,我们不能依靠任何一种自动装配的语义,因为每个容器的做法都不一样。因此,您需要告诉Entlib要调用哪个构造函数,以及应该注入哪些依赖项。这是通过配置数据类完成的。

Entlib 5 is designed to be container independent. As such, we can't rely on any kind of auto-wiring semantics because each container does it differently. So you need to tell Entlib which constructor to call, and what dependencies should be injected. This is done through your configuration data class.

我拍了一个简单的例子,这是跟踪侦听器-没什么特别的:

I slapped together a quick example. Here's the trace listener - not much special:

[ConfigurationElementType(typeof(GuiTraceListenerData))]
public class GuiTraceListener : TraceListener
{
    private readonly ILogFormatter formatter;
    private readonly IGuiController guiController;

    public GuiTraceListener()
        : this(string.Empty, null, null)
    {
    }

    public GuiTraceListener(string name)
        : this(name, null, null)
    {
    }

    public GuiTraceListener(string name, ILogFormatter formatter, IGuiController guiController) : base(name)
    {
        this.formatter = formatter;
        this.guiController = guiController;
    }

    public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id,
        object data)
    {
        if ((Filter == null) || Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
        {
            if (data is LogEntry)
            {
                if (formatter != null)
                {
                    Write(formatter.Format(data as LogEntry));
                }
                else
                {
                    base.TraceData(eventCache, source, eventType, id, data);
                }
            }
            else
            {
                base.TraceData(eventCache, source, eventType, id, data);
            }
        }
    }

    public override void Write(string message)
    {
        guiController.Log(message);
    }

    public override void WriteLine(string message)
    {
        guiController.Log(message);
    }
}

有趣的部分在GuiTraceListenerData类中:

The interesting part is in the GuiTraceListenerData class:

public class GuiTraceListenerData : TraceListenerData
{
    private const string formatterNameProperty = "formatter";

    public GuiTraceListenerData()
        : this("unnamed", null, TraceOptions.None)
    {
    }

    public GuiTraceListenerData(string name)
        : this(name, null, TraceOptions.None)
    {
    }

    public GuiTraceListenerData(string name, string formatterName)
        : this(name, formatterName, TraceOptions.None)
    {
    }

    protected GuiTraceListenerData(string name, string formatterName, TraceOptions traceOutputOptions)
        : base(name, typeof (GuiTraceListener), traceOutputOptions, SourceLevels.All)
    {
        ListenerDataType = typeof (GuiTraceListenerData);
        Formatter = formatterName;
    }

    [ConfigurationProperty(formatterNameProperty, IsRequired = false)]
    [Reference(typeof(NameTypeConfigurationElementCollection<FormatterData, CustomFormatterData>), typeof(FormatterData))]
    public string Formatter
    {
        get { return (string) base[formatterNameProperty]; }
        set { base[formatterNameProperty] = value; }
    }

    protected override Expression<Func<TraceListener>> GetCreationExpression()
    {
        return () =>
            new GuiTraceListener(Name,
                Container.ResolvedIfNotNull<ILogFormatter>(Formatter),
                Container.Resolved<IGuiController>());
    }
}

特别是,请查看该GetCreationExpression方法。告诉entlib,要创建由该配置new表示的对象,调用该构造函数,并解析格式程序(如果已指定)和IGuiController。

In particular, look at that GetCreationExpression method. that's telling entlib that to create the object represented by this config new, call that constructor, and resolve the formatter (if one is specified) and the IGuiController.

然后,在我的测试应用程序(为了快速起见,我使用Winforms)是这样初始化容器和应用程序的:

Then, in my test app (I used Winforms just to be quick) I initialized my container and app like this:

static void Main()
{
    var container = new UnityContainer()
        .AddNewExtension<EnterpriseLibraryCoreExtension>()
        .RegisterType<IGuiController, MessageBoxGuiController>();

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(container.Resolve<Form1>());
}

我的Form1类将LogWriter作为构造函数参数。

My Form1 class takes a LogWriter as a constructor parameter.

关于Entlib 5的构建方式的一件好事是,您在执行此操作时几乎获得了自动配置工具支持-无需编写单独的配置节点。您只需运行时config元素-只需使用config工具将DLL复制到同一目录中,然后它将它拾取即可。

The nice thing about how Entlib 5 is built is that you get almost automatic config tool support when doing this - no need to write a separate config node. Your runtime config element is all you need - just copy the DLL into the same directory with the config tool and it'll just pick it up.

无论如何,从这里开始

希望这会有所帮助。如果您需要更多详细信息,请给我一行,然后我可以将整个工作项目发送给您。

Hope this helps. If you want more details, drop me a line and I can send you the entire working project.

-Chris

这篇关于Unity / EntLib:将依赖项注入CustomTraceListener的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆