1

I'm trying to pull out the Roles below into an IEnumerable<KeyValuePair<int, string>>

<PROJECT PROJECT_NO="161917"> 
  <CONTACT CLIENT_ID="030423253272735482765C" CONTACT_NO="1"> 
    <ROLE ROLE_ID="2" ROLE_DESC="ARCHITECT" /> 
    <ROLE ROLE_ID="5" ROLE_DESC="INTEGRATOR" /> 
  </CONTACT>
</PROJECT>



private static ProjectContact BuildProjectContactFromXml(XElement xml)
    {
        ProjectContact projectContact = new ProjectContact();
        projectContact.ProjectId = SafeConvert.ToInt32(xml.Attribute("PROJECT_NO").Value);
        projectContact.Roles = xml.Elements()
                                    .First()
                                    .Elements()
                                    .Select(role => new KeyValuePair<int, string>(
                                                            SafeConvert.ToInt32(role.Attribute("ROLE_ID").Value), 
                                                            role.Attribute("ROLE_DESC").Value));

        return projectContact;
    }

My question is about deferred execution of this Linq statement. It is my understanding that when I return this ProjectContact, the Linq statement has not yet executed. Is there a way to force the execution of this statement so that it happens in this method, rather than when someone tries to access the Roles? I think I could force the execution of the statement by calling .Count() on it, but it seems like there should be a better way.

JSprang
  • 12,481
  • 7
  • 30
  • 32
  • Sorry, wasn't finished with the question and it posted! I'll finish writing my question, one minute :) – JSprang Aug 06 '10 at 15:49
  • Roles is a IEnumerable>, sorry about that. – JSprang Aug 06 '10 at 15:59
  • In .NET 4 they now have tuples so we don't have to do lists of keyvaluepairs anymore for these things. new Tuple(int value1, string value2) – Jimmy Hoffa Aug 06 '10 at 16:06
  • @Jimmy Hoffa, cool, didn't know about that, but I'm currently working in a 3.5 project. Thanks! – JSprang Aug 06 '10 at 16:14
  • @Jimmy Hoffa: I believe the syntax is actually `Tuple.Create(int value1, string value2)` (types inferred from paramters without being specified) – James Curran Aug 06 '10 at 16:21

2 Answers2

2

projectContact.Roles is going to be a IEnumerable<KeyValuePair<int, string>> is that what you want, or do you want it as a List or DIctionary? For a List, just tack .ToList() at the end of the statement.

For a Dictionary, it's a bit trickier:

   projectContact.Roles = xml.Elements() 
                                .First() 
                                .Elements()
                                .ToDictionary(
                 role=> SafeConvert.ToInt32(role.Attribute("ROLE_ID").Value),
                 role=> role.Attribute("ROLE_DESC").Value)); 

UPDATE: In you comments you state that Roles id IEnumerable<KeyValuePair<int, string>>. Technically, that could be either a Dictionary or a List, although in the former case, you really can't use it's Dictionary-ness via that interface. For that matter, you can use much of it's List-ness in the latter case -- but it seems you specifically don't want that ability.

So, tack on .ToList();. It will be a List behind the scenes, but without going to extradinary lengths, users will still only be able to use it as an IEnumerable.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • On the ProjectContact, I do want the Roles to be an IEnumerable. The reason being is that I don't want the user of the object to be able to edit the collection of Roles. If I made it a List or a Dictionary, they would be able to Add() to it. Does that make sense? – JSprang Aug 06 '10 at 16:07
2

If the datatype of .Roles is array, you could simply append .ToArray() after your .Select(), and that would ensure that the query is executed.

Either way, you can execute a ToList() or .ToArray(), and you'll get execution of the query.

p.campbell
  • 98,673
  • 67
  • 256
  • 322