LINQ 4 XML - Как правильно делать запросы глубоко в древовидной структуре? - PullRequest
0 голосов
/ 25 марта 2010

У меня есть структура XML глубиной 4:

<?xml version="1.0" encoding="utf-8"?>
<EmailRuleList xmlns:xsd="EmailRules.xsd">
  <TargetPST name="Tech Communities">
    <Parse emailAsList="true" useJustDomain="false" fromAddress="false" toAddress="true">
      <EmailRule address="@aspadvice.com" folder="Lists, ASP" saveAttachments="false" />
      <EmailRule address="@sqladvice.com" folder="Lists, SQL" saveAttachments="false" />
      <EmailRule address="@xmladvice.com" folder="Lists, XML" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="false" toAddress="true">
      <EmailRule address="northcoloradoarchitects@googlegroups.com" folder="Special Interest Groups|Northern Colorado Architects Group" saveAttachments="false" />
      <EmailRule address="spambayes@python.org" folder="Support|SpamBayes" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="true" toAddress="false">
      <EmailRule address="support@godaddy.com" folder="Support|GoDaddy" saveAttachments="false" />
      <EmailRule address="renew@no-ip.com" folder="Support|No-IP.com" saveAttachments="false" />
      <EmailRule address="discuss@orchardproject.net" folder="Discussions|Orchard Project" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="true" fromAddress="true" toAddress="false">
      <EmailRule address="@agilejournal.com"     folder="Newsletters|Agile Journal" saveAttachments="false"/>
      <EmailRule address="@axosoft.ccsend.com"   folder="Newsletters|Axosoft Newsletter" saveAttachments="false"/>
      <EmailRule address="@axosoft.com"          folder="Newsletters|Axosoft Newsletter" saveAttachments="false"/>
      <EmailRule address="@cmcrossroads.com"     folder="Newsletters|CM Crossroads" saveAttachments="false" />
      <EmailRule address="@urbancode.com"        folder="Newsletters|Urbancode" saveAttachments="false" />
      <EmailRule address="@urbancode.ccsend.com" folder="Newsletters|Urbancode" saveAttachments="false" />
      <EmailRule address="@Infragistics.com"     folder="Newsletters|Infragistics" saveAttachments="false" />
      <EmailRule address="@zdnet.online.com"     folder="Newsletters|ZDNet Tech Update Today" saveAttachments="false" />
      <EmailRule address="@sqlservercentral.com" folder="Newsletters|SQLServerCentral.com" saveAttachments="false" />
      <EmailRule address="@simple-talk.com"      folder="Newsletters|Simple-Talk Newsletter" saveAttachments="false" />
    </Parse>
  </TargetPST>
  <TargetPST name="[Sharpen the Saw]">
    <Parse emailAsList="false" useJustDomain="false" fromAddress="false" toAddress="true">
      <EmailRule address="rmiug-jobs@yahoogroups.com" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="inkedinusmc@yahoogroups.com" folder="Social|LinkedIn USMC" saveAttachments="false"/>
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="true" toAddress="false">
      <EmailRule address="JobAlerts@CyberCoders.com" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="jobs@dice.com" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="news@cruisecritic.com" folder="Social|Cruise Critic" saveAttachments="false"/>
    </Parse>
    <Parse emailAsList="false" useJustDomain="true" fromAddress="true" toAddress="false">
      <EmailRule address="@moody.edu" folder="Social|5 Love Languages" saveAttachments="false" />
      <EmailRule address="@postmaster.twitter.com" folder="Social|Twitter" saveAttachments="false"/>
      <EmailRule address="@diabetes.org" folder="Physical|American Diabetes Association" saveAttachments="false"/>
      <EmailRule address="@membership.webshots.com" folder="Social|Webshots" saveAttachments="false"/>
    </Parse>
  </TargetPST>
</EmailRuleList>

Теперь у меня есть и FromAddress, и ToAddress, которые анализируются из входящего электронного письма. Я хотел бы сделать запрос LINQ к набору классов, который был десериализован из этого XML. Например: ToAddress = asp@aspadvice.com FromAddress = keithb@sol3.net

Запрос:

  • Получить EmailRule.Include (Parse) .Include (TargetPST), где address == ToAddress AND Parse.ToAddress == true AND Parse.useJustDomain == false
  • Получить EmailRule.Include (Parse) .Include (TargetPST), где адрес == [Только адрес домена] И Parse.ToAddress == true И Parse.useJustDomain == true
  • Получить EmailRule.Include (Parse) .Include (TargetPST) где address == FromAddress AND Parse.FromAddress == true AND Parse.useJustDomain == false
  • Получить EmailRule.Include (Parse) .Include (TargetPST), где адрес == [Только из домена адреса] И Parse.FromAddress == true И Parse.useJustDomain == true

Мне трудно разобраться в этом запросе LINQ. Я могу, конечно, зацикливаться на всех битах в XML следующим образом (включая десериализацию в объекты):

XmlSerializer s = new XmlSerializer(typeof(EmailRuleList));
TextReader r = new StreamReader(path);
_emailRuleList = (EmailRuleList)s.Deserialize(r);

TargetPST[] PSTList = _emailRuleList.Items;
foreach (TargetPST targetPST in PSTList)
{
    olRoot = GetRootFolder(targetPST.name);
    if (olRoot != null)
    {
        Parse[] ParseList = targetPST.Items;
        foreach (Parse parseRules in ParseList)
        {
            EmailRule[] EmailRuleList = parseRules.Items;
            foreach (EmailRule targetFolders in EmailRuleList)
            {
            }
        }
    }
}

Однако это означает прохождение всех этих циклов для каждого адреса. Для меня имеет больше смысла делать запросы к объектам. Любые советы приветствуются!

1 Ответ

1 голос
/ 25 марта 2010

Этот код у вас здесь:

XmlSerializer s = new XmlSerializer(typeof(EmailRuleList)); 
TextReader r = new StreamReader(path); 
_emailRuleList = (EmailRuleList)s.Deserialize(r); 

TargetPST[] PSTList = _emailRuleList.Items; 
foreach (TargetPST targetPST in PSTList) 
{ 
    olRoot = GetRootFolder(targetPST.name); 
    if (olRoot != null) 
    { 
        Parse[] ParseList = targetPST.Items; 
        foreach (Parse parseRules in ParseList) 
        { 
            EmailRule[] EmailRuleList = parseRules.Items; 
            foreach (EmailRule targetFolders in EmailRuleList) 
            { 
            } 
        } 
    } 
} 

В LINQ это просто следующее:

var query = from targetPST in _emailRuleList.Items
            let olRoot = GetRootFolder(targetPST.name)
            where olRoot != null
            from parseList in targetPST.Items
            from emailRule in parseList.Items
            select [whatever you want to select];

Отсюда, просто включите соответствующие предложения where. На каждом «уровне» у вас есть доступ к объектам, ранее указанным в предложении from, поэтому, если вы хотите выполнить один из ваших запросов (ваш первый), это будет примерно так:

where emailRule.address == ToAddress && 
    parseList.toAddress == true &&
    parseList.useJustDomain == false
select new {
    EmailRule = emailRule,
    Parse = parseList,
    TargetPST = targetPST
}

Чтобы сделать это в методах, я думаю, что вы приносите больше вреда, чем пользы (особенно в удобочитаемости), но здесь это так. Важно отметить, почему это так сложно, потому что вложенный набор предложений FROM преобразуется в SelectMany, и, в конечном счете, вам необходим EmailRule со связанными родительскими объектами, вы должны сделать весь выбор внутри набора вложенных лямбд. чтобы иметь ссылки на родительские объекты (поскольку сами дочерние объекты не имеют обратных ссылок).

_emailRuleList
    .Where( targetPst => GetRootFolder( targetPst.Name ) != null )
    .SelectMany( targetPst => {
        return targetPst.Items.SelectMany( parse => {
            return parse.Items.Select( rule => {
                return new {
                    TargetPST = targetPst,
                    Parse = parse,
                    EmailRule = rule
                };
            } );
        } );
    } )
    .Where( x => x.EmailRule.address == ToAddress &&
                 x.Parse.toAddress == true &&
                 x.Parse.useJustDomain == false );
...