views:

107

answers:

1

I have an XML structure that is 4 deep:

<?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="[email protected]" folder="Special Interest Groups|Northern Colorado Architects Group" saveAttachments="false" />
      <EmailRule address="[email protected]" folder="Support|SpamBayes" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="true" toAddress="false">
      <EmailRule address="[email protected]" folder="Support|GoDaddy" saveAttachments="false" />
      <EmailRule address="[email protected]" folder="Support|No-IP.com" saveAttachments="false" />
      <EmailRule address="[email protected]" 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="[email protected]" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="[email protected]" folder="Social|LinkedIn USMC" saveAttachments="false"/>
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="true" toAddress="false">
      <EmailRule address="[email protected]" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="[email protected]" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="[email protected]" 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>

Now, I have both an FromAddress and a ToAddress that is parsed from an incoming email. I would like to do a LINQ query against a class set that was deserialized from this XML. For instance: ToAddress = [email protected] FromAddress = [email protected]

Query:

  • Get EmailRule.Include(Parse).Include(TargetPST) where address == ToAddress AND Parse.ToAddress==true AND Parse.useJustDomain==false
  • Get EmailRule.Include(Parse).Include(TargetPST) where address == [ToAddress Domain Only] AND Parse.ToAddress==true AND Parse.useJustDomain==true
  • Get EmailRule.Include(Parse).Include(TargetPST) where address == FromAddress AND Parse.FromAddress==true AND Parse.useJustDomain==false
  • Get EmailRule.Include(Parse).Include(TargetPST) where address == [FromAddress Domain Only] AND Parse.FromAddress==true AND Parse.useJustDomain==true

I am having a hard time figuring this LINQ query out. I can, of course, loop on all the bits in the XML like so (includes deserialization into objects):

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)
            {
            }
        }
    }
}

However, this means going through all these loops for each and every address. It makes more sense to me to query against the Objects. Any tips appreciated!

+1  A: 

This code you have here:

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) 
            { 
            } 
        } 
    } 
} 

Is effectively just the following in 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];

From here, just include the appropriate where clauses. At every "level" you have access to the objects previously specified in a from clause, so if you wanted to do one of your queries (your first one), it'd be something like this:

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

To do this in methods, I think you're doing more harm than good (esp in readability), but here it is. The important thing to note on why this is so complicated is that a nested set of FROM clauses translates into a SelectMany, and because ultimately you need the EmailRule with its associated parent objects, you have to do all your selecting inside a set of nested lambdas in order to have references to the parent objects (since the child objects themselves do not have backreferences).

_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 );
Adam Sills
anyway to do something like: var pRules = _emailRuleList.Items.Where(a => a.Items.Where(b => b.Items.Where(c => c.address == toAddress))).ToList();
Keith Barrows
Well, aside from that being much harder to read, yes, you can sort of do that. But you can't nest the WHERE methods like that. The problem is you want the EmailRule with its Parse and TargetPST. What you've got here is the TargetPSTs where all the various conditions are true.
Adam Sills
If you want to structure your query using a set of methods, I think you're going to make a harder time for yourself just to get the backreferences working correctly (to do so, the entire thing must be a set of nested lambdas, as you are doing) whereas if you use the LINQ query it handles that for you.
Adam Sills
Yea. Found that out. Also fought with returning anonymous types from a method. lol. Since the variables change but the linq statement does not, I moved it to a method. I need to return 2 stings and a bool out of the entire mess so created a class and dropped the var in favor of a defined class. Works like a champ!
Keith Barrows