3

I have a component that depends on a list of strings:

// ctor
public MyComponent(IList<string> someStrings) { ... }

Using Castle Windsor, I'm trying to provide this dependency from the AppSettings section of my app.config file like so:

container.Register(Component
    .For<IMyComponent>()
    .ImplementedBy<MyComponent>()
    .DependsOn(Dependency.OnAppSettingsValue("someStrings", "SomeStrings"))
);

The Windsor documentation on Inline Dependencies says:

appSettings and conversion: Values in the config file are stored as text, yet the dependencies in your code may be of other types (TimeSpan in this example). Windsor has got you covered, and for most cases will perform the appropriate conversion for you.

  1. Does this cover my case where I'd like to convert to IList<string>?
  2. If so, what is the correct way to enter the list of strings in the app.config file?
  3. If not, is there an extensibility point where I can specify my own conversion?
urig
  • 16,016
  • 26
  • 115
  • 184

1 Answers1

2

The bigger issue here is that it's not trivial to store a list of values in appSettings. This question addresses it and the best answer is to create your own section in the settings file, which you then have to access via ConfigurationManager.GetSection() instead of ConfigurationManager.AppSettings.Get(), which is what Dependency.OnAppSettingsValue() uses. Looking through the rest of the Dependency class, it seems there's no built-in way to do this.

However, if it really is just strings that you need, you have at least two options that aren't all that bad (in my opinion).

1. Store the strings in your App.config file using a StringCollection.

This is just a shorter, built-in version of creating your own section in App.config. Use the editor in Visual Studio's Project Properties page to add an application setting of type StringCollection. This will make your App.config look something like this:

<configuration>
  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="YourApplication.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <applicationSettings>
    <YourApplication.Properties.Settings>
      <setting name="SomeStrings" serializeAs="Xml">
        <value>
          <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <string>one</string>
            <string>two</string>
            <string>three</string>
          </ArrayOfString>
        </value>
      </setting>
    </YourApplication.Properties.Settings>
  </applicationSettings>
</configuration>

Then when configuring your component:

// StringCollection only implements IList, so cast, then convert
var someStrings = Properties.Settings.Default.SomeStrings.Cast<string>().ToArray();
container.Register(Component.For<IMyComponent>()
                            .ImplementedBy<MyComponent>()
                            .LifestyleTransient()
                            .DependsOn(Dependency.OnValue<IList<string>>(someStrings)));

2. Store the strings as a delimited list in appSettings, then manually split them.

This is likely the simpler approach, though assumes you can figure out a delimiter for your strings (which may not always be the case). Add the values to your App.config file:

<configuration>
  <appSettings>
    <add key="SomeStrings" value="one;two;three;four" />
  </appSettings>
</configuration>

Then when configuring your component:

var someStrings = ConfigurationManager.AppSettings["SomeStrings"].Split(';');
container.Register(Component.For<IMyComponent>()
                            .ImplementedBy<MyComponent>()
                            .LifestyleTransient()
                            .DependsOn(Dependency.OnValue<IList<string>>(someStrings)));

In either case, we're just adding a small amount of work on top of Dependency.OnValue, which is all Dependency.OnAppSettingsValue does anyways.

I think this answers your questions, but to be explicit:

  1. Yes, but you're doing the conversion yourself so you can convert to anything you'd like.
  2. See the linked question and accepted answer, or use a StringCollection.
  3. Dependency.OnValue is the extension (in my opinion), and I don't know of or see any other place where you would do it. However, given the steps above I don't think this is necessary.
Community
  • 1
  • 1
Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • Many thanks for the detailed answer - I've gone with option 2 for now. – urig Mar 02 '16 at 15:22
  • Actually, I think the "Yes, but" answer to my first question is a "no". While Windsor lets me (out of the box) put a TimeSpan value in appSettings, it does not let me do the same for a List (or string[]) value. I'd like to figure out where Windsor does the "deserialization" from the string value to TimeSpan and see if that can be extended to List. – urig Mar 02 '16 at 15:28
  • The convesions subsystem looks the place where the magic happens: https://github.com/castleproject/Windsor/blob/master/docs/subsystems.md but not much documentation for it that I can find. – urig Mar 02 '16 at 15:37
  • 1
    For future reference: @mark-seemann has a blog post touching close to this topic: http://blog.ploeh.dk/2012/11/07/AppSettingsconventionforCastleWindsor/ – urig Mar 02 '16 at 16:01
  • Good finds. It looks like you probably could create an `ISubDependencyResolver` like in Mark's post to do this in a more "Windsory" kind of way. – Patrick Quirk Mar 02 '16 at 16:09