ConfigurationElementCollection和Linq

34

我编写了一些自定义的配置集合、元素等等。现在,我想要做一个简单的Linq语句:

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

我遇到了以下错误:

无法找到“MyNamespace.ServerDetails”类型的查询模式实现。未找到“Where”。

ServerElement 具有两个属性:

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; }
    }
}

服务器详细信息

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;
    }
}

我是否漏掉了什么?我需要添加些内容才能使用自定义配置元素与 Linq 吗?

顺便说一句,我已经定义了 using System.Linq;,因为在同一个类中的其他地方也在使用它。


服务器详细信息类(ServerDetails)长什么样? - Jon Skeet
4个回答

41

好的,鉴于它全部是弱类型,你需要显式调用Cast<>OfType<>,或者为范围变量指定一个明确的类型。你还需要在ServerDetails上指定ServerCollection属性。例如:

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

1
这就是为什么我通常在ConfigurationElementCollections上提供一个Children属性 - 奇怪的是,明确实现IEnumerable<ServerDetails>并不能解决问题。 - Jonathan Dickinson
嗨Jon,首先感谢您抽出时间查看我的问题。这个答案很棒,但是我不得不将 from ServerDetails 改为 from ServerElement,然后它就可以工作了 :o) 谢谢!! - Neil Knight

24

使用Brian Gideon的简单例子中的yield return,我能够枚举我的ConfigurationElementCollection。

它可能看起来像这样(使用原始问题):

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

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

尽管我没有遇到以下错误:

无法找到源类型"MyNamespace.ServerDetails"的查询模式的实现。未找到“Where”。

但是,我也无法使用LINQ迭代我的ConfigurationElementCollection。这个解决方案解决了我的问题,让我可以使用LINQ来迭代我的集合。


4
 var server = ((ServerDetails) ConfigurationManager.GetSection("serverDetails")).
      ServerCollection.Cast<ServerElement>().FirstOrDefault(x => x.Name == serverName);

1
请尽量提供一个关于您解决方案的良好描述。 参见:如何撰写一个好答案?。谢谢。 - 4b0

0
一个非常晚的回答,我会使用这个扩展类将任何 ConfigurationElementCollection 安全地转换为 IEnumerable。
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;
        }
    }
}

以下是示例用法

ConfigurationManager
   .GetSection("serverDetails"))
   .ServerCollection
   .ToEnumerable<ServerElement>()
   .FirstOrDefault(x => x.Name == serverName);

1
我喜欢@Xiaoguo Ge的解决方案,但必须添加小修复。yield return default; 应该在else分支中: else yield return default; - KOLRH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接