ConfigurationElementCollection and Linq
Asked Answered
A

4

34

I've written some custom configuration collections, elements etc. Now, I'd like to do a simple Linq statement:

ServerDetails servers = ConfigurationManager.GetSection("serverDetails") as ServerDetails;
var server = from s in servers
             where s.Name == serverName
             select s;

I get the error:

Could not find an implementation of the query pattern for source type 'MyNamespace.ServerDetails'. 'Where' not found.

The ServerElement has two properties:

public class ServerElement : ConfigurationElement
{
    [ConfigurationProperty("ip")]
    public string IP
    {
        get { return (string)base["ip"]; }
        set { base["ip"] = value; }
    }

    [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
    public string Name
    {
        get { return (string)base["name"]; }
        set { base["name"] = value; }
    }
}

ServerDetails

public sealed class ServerDetails : ConfigurationSection
{
    [ConfigurationProperty("ServerCollection")]
    [ConfigurationCollection(typeof(ServerCollection), AddItemName = "add")]
    public ServerCollection ServerCollection
    {
        get { return this["ServerCollection"] as ServerCollection; }
    }
}

ServerCollection

public sealed class ServerCollection : ConfigurationElementCollection
{
    public void Add(ServerElement ServerElement)
    {
        this.BaseAdd(ServerElement);
    }

    public override ConfigurationElementCollectionType CollectionType
    {
        get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
    }

    protected override ConfigurationElement CreateNewElement()
    {
        return new ServerElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((ServerElement)element).Name;
    }
}

Am I missing something? Do I need to add something in so that I can use Linq with a custom configuration element?

By the way, I have using System.Linq; defined as I'm using it else where within the same class.

Ashtoreth answered 7/12, 2011 at 10:22 Comment(1)
What does the ServerDetails class look like?Spongioblast
S
41

Okay, given that it's all weakly typed, you'll need to either call Cast<> or OfType<> explicitly, or give an explicit type to the range variable. You'll also need to specify the ServerCollection property on your ServerDetails. For example:

ServerDetails servers = (ServerDetails) ConfigurationManager.GetSection("serverDetails");
var server = from ServerElement s in servers.ServerCollection
             where s.Name == serverName
             select s;
Spongioblast answered 7/12, 2011 at 10:33 Comment(2)
This is why I usually provide a Children property on ConfigurationElementCollections - strangely enough explicitly implementing IEnumerable<ServerDetails> does not do the trick.Butterball
Hi Jon, firstly thanks for taking the time to look at my question. This answer is great, but I had to change the from ServerDetails to from ServerElement and it works :o) Thank you!!Ashtoreth
A
24

Using Brian Gideon's simple example of yield return in his IEnumerable<T> implementation, I was able to enumerate over my ConfigurationElementCollection.

It would look something like this (using the original question):

public sealed class ServerCollection : ConfigurationElementCollection,
    IEnumerable<ServerElement>
{
    ...

    public new IEnumerator<ServerElement> GetEnumerator()
    {
        foreach (var key in this.BaseGetAllKeys())
        {
            yield return (ServerElement)BaseGet(key);
        }
    }
}

While I was NOT getting the error:

Could not find an implementation of the query pattern for source type 'MyNamespace.ServerDetails'. 'Where' not found

...I was not able to iterate over my ConfigurationElementCollection using LINQ, either. This solution fixed my problem so that I could use LINQ to iteration over my collection.

Astragalus answered 8/4, 2015 at 20:10 Comment(0)
G
4
 var server = ((ServerDetails) ConfigurationManager.GetSection("serverDetails")).
      ServerCollection.Cast<ServerElement>().FirstOrDefault(x => x.Name == serverName);
Garofalo answered 9/9, 2018 at 1:33 Comment(1)
Please try to provide a nice description about how your solution works. See: How do I write a good answer?. Thanks.Teazel
A
0

A very late answer, I would use this extension class to turn any ConfigurationElementCollection into an IEnumerable safely.

public static class ConfigurationElementCollectionExtension
{
    public static IEnumerable<T> ToEnumerable<T>(this ConfigurationElementCollection collection)
    {
        foreach (var element in collection)
        {
            if (element is T)
                yield return (T)element;

            yield return default;
        }
    }
}

Example usage below

ConfigurationManager
   .GetSection("serverDetails"))
   .ServerCollection
   .ToEnumerable<ServerElement>()
   .FirstOrDefault(x => x.Name == serverName);
Alodium answered 30/3, 2020 at 5:39 Comment(1)
I like @Xiaoguo Ge's solution, but have to add small fix. yield return default; should be in an else branch: else yield return default;Teasel

© 2022 - 2024 — McMap. All rights reserved.