Unity / EntLib: внедрение зависимости в CustomTraceListener - PullRequest
2 голосов
/ 09 августа 2010

Извините, это совершенно особая тема, поэтому она может не интересовать многих.: - (

Однако мне нужно сделать следующее:

  • У меня есть приложение, которое обеспечивает ведение журнала в каком-либо консольном окне (это окно WPF,из-за требований приложения и потому что приложение должно выглядеть роскошно даже здесь - наш специальный клиент спрашивал об этом и говорит об этом каждый раз, когда мы встречаемся)

  • Чтобы обеспечить независимую от потоков запись в журнал, которую я создалпара интерфейс / реализация "IGUIController" / "GUIController"

Пока все хорошо. Все хорошо.

Однако:

  • Мне нужен мой собственный прослушиватель трассировки (я называю его «GUITraceListener»), который использует IGUIController-интерфейс для записи определенных сообщений журнала в это роскошное WPF-окно

Пока мое решение состоит в том, чтобывзломанное, старое skool code запах "синглтон" в стиле "Current" - свойство моего GUIController (да, и мне стыдно, и я действительно знаю, что это ужасно), которое я вызываю так:

GUIController.Current.Log(message);

Очевидно, что это не пойдет.

Итак, правильный способ сделать это - ввести зависимость (конечно).Однако, когда я это делаю, я получаю жалобу (время выполнения), что я не предоставляю конструктору 0 (ноль) аргументов для моего GUITraceListener - класса.

На самом деле, я понимаю, чтоздесь:

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

И жалоба такова:

ArgumentException не обработан. Невозможно найти соответствующий конструктор 0 аргументов для GUITraceListener

Это действительно плохо.Я имею в виду, что Unity является частью Enterprise Library, почему эти парни просто не добавили возможность внедрения моих зависимостей?

Уточнение:

В итоге я хочу получить следующее:

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

1 Ответ

6 голосов
/ 22 августа 2010

Entlib 5 имеет возможность сделать это.Я предполагаю, что у вас есть [ConfigurationElementType (typeof (CustomTraceListenerData))] в вашем классе слушателя, верно?

Entlib 5 разработан, чтобы быть независимым от контейнера. Таким образом, мы не можем полагаться ни на какой видсемантики автоматического подключения, потому что каждый контейнер делает это по-своему. Поэтому вам нужно указать Entlib, какой конструктор вызывать, и какие зависимости нужно внедрить. Это делается с помощью вашего класса данных конфигурации.

Я быстро собрал все вместеНапример, вот слушатель трассировки - не особо особенный:

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

Мои классы Form1s принимает LogWriter в качестве параметра конструктора.

Хорошая вещь о том, как построен Entlib 5, заключается в том, что вы получаете почти автоматическую поддержку инструмента конфигурации при этом - не нужно писать отдельный узел конфигурации.Ваш элемент конфигурации времени выполнения - это все, что вам нужно - просто скопируйте DLL в тот же каталог с помощью инструмента конфигурации, и он просто заберет его.

В любом случае, с этого момента он просто работает.

Надеюсь это поможет.Если вам нужны подробности, напишите мне, и я отправлю вам весь рабочий проект.

-Chris

...