Как сделать этот LINQ to XML запрос более элегантным? - PullRequest
2 голосов
/ 01 марта 2010

У меня есть коллекция записей MODS, которая выглядит следующим образом:

<modsCollection>
<mods [namespaces etc] >
    <genre authority="diva" type="contentType" lang="eng">Other academic</genre>
    <genre authority="diva" type="contentType" lang="swe">Övrigt vetenskapligt</genre>
    <name type="personal">
      <namePart type="family">Svensson</namePart>
      <namePart type="given">Sven</namePart>
      <namePart type="date">1880-</namePart>
      <role>
        <roleTerm type="code" authority="marcrelator">aut</roleTerm>
      </role>
      <affiliation>Stockholms universitet, institutionen institutionen</affiliation>
    </name>
[...]
</mods>
<mods/>
<mods/>
</modsCollection>

Мой запрос LINQ для поиска в коллекции записей, связанных с определенным человеком с определенной ролью, выглядит следующим образом:

XElement[] hits = (from record in x.Root.Elements(modsV3 + "mods").Elements(modsV3 + "name")
    from r1 in record.Elements(modsV3+"namePart")
    where  
        r1.HasAttributes && 
        r1.Attribute("type").Value == "family" &&
        r1.Value == familyName
   from r2 in record.Elements(modsV3 + "namePart")
   where
       r2.HasAttributes &&
       r2.Attribute("type").Value == "given" &&
       r2.Value == givenName
   from r3 in record.Elements(modsV3 + "role").Elements(modsV3+"roleTerm")
   where
       r3.HasAttributes &&
       r3.Attribute("type").Value == "code" &&
       r3.Value == "aut"
   select r1.Parent.Parent).ToArray<XElement>();    

Я думаю, что этот запрос мог бы быть лучше написан. Как?

Ответы [ 2 ]

2 голосов
/ 04 марта 2010

Я бы использовал синтаксис метода Extension и сделал бы методы фильтра Linq, как показано ниже:

    private static XElement[] GetHits(XDocument x, string modsV3, string givenName, string familyName)
    {

        return x.Root.Elements(modsV3 + "mods")
            .MatchNamePart("given", givenName)
            .MatchNamePart("family", familyName).ToArray();
    }

    private static string modsV3 = "whatever";

    private static IEnumerable<XElement> MatchNamePart(this IEnumerable<XElement> records, string type, string givenName)
    {
        return records.Where(rec => rec.Element(modsV3 + "name").
            Elements(modsV3 + "namePart").Any(r1 => HasAttrib(r1, type, givenName)));
    }

    private static bool HasAttrib(XElement element, string attribName, string value)
    {
        return  element.HasAttributes &&
                element.Attribute("type").Value == attribName &&
                element.Value == value;
    }

Это только соответствует имени. Но вы можете использовать эти методы в качестве строительных блоков. Вы можете повторно использовать совпадение части имени везде, где вы запрашиваете этот тип документа, поэтому не подлежащая повторному использованию часть мала. Вторая половина запроса может быть получена из этого примера.

2 голосов
/ 04 марта 2010

Я знаю, что вы хотите использовать только LINQ, но, возможно, сочетание с XPath упрощает ваш код:

XElement[] hits = (from record in config.Elements(modsV3 + "mods").Elements(modsV3 + "name")
where   record.XPathSelectElement(string.Format("./{0}namePart[@type='family' and .='{1}']", modsV3, familyName)) != null &&
        record.XPathSelectElement(string.Format("./{0}namePart[@type='given' and .='{1}']", modsV3, givenName)) != null &&
        record.XPathSelectElement(string.Format("./{0}role/{0}roleTerm[@type='code' and .='aut']", modsV3)) != null
select record.Parent).ToArray<XElement>(); 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...