unity-container - 统一/EntLib : Injecting a dependency into a CustomTraceListener

标签 unity-container enterprise-library ioc-container

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

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

  • 我有一个应用程序,它提供对某种控制台窗口的日志记录(它是一个 WPF 窗口,因为应用程序要求,并且应用程序即使在这里也需要看起来华丽 - 我们的特殊客户要求并讨论它每次我们见面)

  • 为了提供与线程无关的日志记录,我创建了一个接口(interface)/实现对“IGUIController”/“GUIController”

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

但是:

  • 我需要自己的自定义跟踪监听器(我称之为“GUITraceListener”),它使用 IGUIController 接口(interface)将特定的日志消息写入华丽的 WPF 窗口

到目前为止,我的解决方案是在我的 GUIController 上有一个被黑的、旧的 skool 代码气味单例 - 样式“Current” - 属性(是的,我很羞愧,我确实知道这很糟糕),我调用它像这样:

GUIController.Current.Log(message);

显然,这是不行的。

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

事实上,我在这里明白了:

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

投诉是:

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

这实在是太糟糕了。我的意思是,Unity 是企业库的一部分,为什么那些人不简单地添加注入(inject)我的依赖项的可能性?

澄清:

我最终想要的是:

    /// <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 被设计为独立于容器。因此,我们不能依赖任何类型的 Autowiring 语义,因为每个容器的做法都不同。因此,您需要告诉 Entlib 要调用哪个构造函数,以及应该注入(inject)哪些依赖项。这是通过您的配置数据类完成的。

我举了一个简单的例子。这是跟踪监听器 - 没什么特别的:

[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 类中:

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。

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

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

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

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

Entlib 5 构建方式的好处是,您在执行此操作时几乎可以获得自动配置工具支持 - 无需编写单独的配置节点。您的运行时配置元素就是您所需要的 - 只需将 DLL 复制到配置工具的同一目录中,它就会拾取它。

无论如何,从这里开始它就可以了。

希望这有帮助。如果您想了解更多详细信息,请给我写信,我可以向您发送整个工作项目。

-克里斯

关于unity-container - 统一/EntLib : Injecting a dependency into a CustomTraceListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3438362/

相关文章:

c# - 我需要 Unity 容器的单个实例吗?

c# - 具有属性的单例类的依赖注入(inject)

c# - 使用 Enterprise Library 4.1 从多个进程记录到同一个文件

c# - 解决共享 transient 实例的位置

mvvm - MvvmCross IoC 中的子容器

c# - IUnityContainer.Resolve<T> 抛出错误,声称它不能与类型参数一起使用

c# - MVVM 和依赖注入(inject)

c# - 记录应用程序 block 不记录到文件

c# - 为什么在调用 ExceptionPolicy.HandleException 时会出现 System.InvalidOperationException?

unit-testing - 我可以在单元/集成测试中使用 IoC 容器吗?