views:

165

answers:

2

In this question Jon Skeet offered a very interesting solution to making a LINQ-to-XML statement dynamic, but my knowledge of lambdas and delegates is not yet advanced enough to implement it:

I've got it this far, but of course I get the error "smartForm does not exist in the current context":

private void LoadWithId(int id)
{
    XDocument xmlDoc = null;
    try
    {
        xmlDoc = XDocument.Load(FullXmlDataStorePathAndFileName);
    }
    catch (Exception ex)
    {
        throw new Exception(String.Format("Cannot load XML file: {0}", ex.Message));
    }

    Func<XElement, bool> whereClause = (int)smartForm.Element("id") == id";

    var smartForms = xmlDoc.Descendants("smartForm")
        .Where(whereClause)
        .Select(smartForm => new SmartForm
                     {
                         Id = (int)smartForm.Element("id"),
                         WhenCreated = (DateTime)smartForm.Element("whenCreated"),
                         ItemOwner = smartForm.Element("itemOwner").Value,
                         PublishStatus = smartForm.Element("publishStatus").Value,
                         CorrectionOfId = (int)smartForm.Element("correctionOfId"),
                         IdCode = smartForm.Element("idCode").Value,
                         Title = smartForm.Element("title").Value,
                         Description = smartForm.Element("description").Value,
                         LabelWidth = (int)smartForm.Element("labelWidth")
                     });

    foreach (SmartForm smartForm in smartForms)
    {
        _collection.Add(smartForm);
    }
}

Ideally I want to be able to just say:

var smartForms = GetSmartForms(smartForm=> (int) smartForm.Element("DisplayOrder").Value > 50);

I've got it this far, but I'm just not grokking the lambda magic, how do I do this?

public List<SmartForm> GetSmartForms(XDocument xmlDoc, XElement whereClause)
{
    var smartForms = xmlDoc.Descendants("smartForm")
        .Where(whereClause)
        .Select(smartForm => new SmartForm
                     {
                         Id = (int)smartForm.Element("id"),
                         WhenCreated = (DateTime)smartForm.Element("whenCreated"),
                         ItemOwner = smartForm.Element("itemOwner").Value,
                         PublishStatus = smartForm.Element("publishStatus").Value,
                         CorrectionOfId = (int)smartForm.Element("correctionOfId"),
                         IdCode = smartForm.Element("idCode").Value,
                         Title = smartForm.Element("title").Value,
                         Description = smartForm.Element("description").Value,
                         LabelWidth = (int)smartForm.Element("labelWidth")
                     });
}
+3  A: 

I expect you mean:

public List<SmartForm> GetSmartForms(
       XDocument xmlDoc, Func<XElement,bool> whereClause)

and:

Func<XElement, bool> whereClause = smartForm => (int)smartForm.Element("id") == id;

To use as a method, I would make it IEnumerable<T>:

public static IEnumerable<SmartForm> GetSmartForms(
       XDocument xmlDoc, Func<XElement,bool> predicate)
    {
        return xmlDoc.Descendants("smartForm")
            .Where(predicate)
            .Select(smartForm => new SmartForm
            {
                ... snip
            });
    }

and call as:

            foreach (SmartForm smartForm in GetSmartForms(xmlDoc,
                sf => (int)sf.Element("id") == id))
            {
                _collection.Add(smartForm);
            }
Marc Gravell
brilliant, this (1) worked, (2) was exactly what I needed, (3) taught me something about lambdas seeing them solve my own issue, thanks!
Edward Tanguay
+1  A: 

I'm just not grokking the lambda magic,

Marc's answer addresses the scenario you have. My answer will attempt to address the lambda magic you are missing.

lambda expressions have three parts: parameters arrow methodbody

Func<int, int, int> myFunc =  (x, i) => x * i ;

For parameters, these must be enclosed in parens except in the case of a single parameter. This section introduces variable names into the scope. Since you didn't supply parameters in your lambda, you didn't have any variable names.

arrow is required, since you didn't have an arrow, the compiler didn't know you were making a lambda.

For methodbody, if it's a one-liner, return is implied. Otherwise, open a curly brace and make a method body like you normally would.

David B