views:

109

answers:

2

I'm working on a quick project to monitor/process data. Essentially that's just monitors, schedules and processors. The monitor checks for data (ftp, local, imap, pop, etc) using a schedule and sends new data to a processor. They call have interfaces.

I'm trying to find a sane way to use config to configure what schedule/processor each monitor uses. That's pretty easy:

<monitor type="any.class.implementing.monitor">
    <schedule type="any.class.implementing.schedule">
        ...
    </schedule>
    <processor type="any.class.implementing.processor" />
</monitor>

What I'm struggling with is what's the best way to configure any old monitor/schedule/processor thrown into the mix. On one hand, one could implement constructor params or properties (give ot take any syntax):

<monitor type="any.class.implementing.monitor">
    <args>
        <arg value="..." />
    </args>
    <properties>
        <property name="..." value=..." />
    </properties>
    <schedule type="any.class.implementing.schedule">
        ...
    </schedule>
    <processor type="any.class.implementing.processor" />
</monitor>

Another solution is factory method in each interface that takes the custom config as a param:

public IMonitor Create(CustomConfigSection config);

I've seen people use both. What do you prefer? Any tricks of the trade when mapping config to constructors?

I'm a little torn as to whether DI can fit into this mess. In the end, it would be a set of bindings per monitor instance, which seems pointless except for defaults, which config could cover.

A: 

When I've done this sort of thing, I've implemented an IConfigurationSectionHandler that basically works as a factory to parse the configuration, create objects of the types specified in the configuration, and return them in some sort of data structure (typically a List or Dictionary). Whether you use an IConfigurationSectionHandler or not, I think the factory is the way to go since you'll localize the code that deals with parsing the configuration file and creating the objects in one class (or one per section).

I also prefer classes with simple constructors and property setter/getters for configuration over classes with lots of parameters in the constructor. This makes it easier for the factory to operate and decreases the coupling between the factory and the class being constructed.

tvanfosson
A: 

First, I would define configuration elements representing the monitors:

public class MonitorElement : ConfigurationElement
{
    // ...whatever schema you prefer...
}

public class MonitorElementCollection : ConfigurationElementCollection
{
    // ...standard implementation...
}

and a configuration section to host them:

public class YourSection : ConfigurationSection
{
    [ConfigurationProperty("monitors")]
    [ConfigurationCollection(typeof(MonitorElementCollection))]
    public MonitorElementCollection Monitors
    {
        get { return (MonitorElementCollection) this["monitors"]; }
    }
}

Then, I would define an interface representing the set of monitors:

public interface IMonitorRepository
{
    IEnumerable<Monitor> GetMonitors();
}

and create an implementation which reads the configuration file:

public sealed class ConfiguredMonitorRepository : IMonitorRepository
{
    private readonly string _sectionName;

    public ConfiguredMonitorRepository(string sectionName)
    {
        _sectionName = sectionName;
    }

    public IEnumerable<Monitor> GetMonitors()
    {
        var section = (YourSection) ConfigurationManager.GetSection(_sectionName);

        if(section != null)
        {
            foreach(var monitor in section.Monitors)
            {
                yield return ...create and configure monitor...
            }
        }
    }
}

This defines where you turn the configuration into actual instances, which was only half your question. I think the XML syntax you have for setting constructor arguments and properties is fine. You might be able to glean some API and implementation ideas from Autofac's XML configuration system.

Really, what you're doing is a forte of IoC containers; you could look into leveraging one of those.

Bryan Watts