1

I'm VERY new to using LINQ. I have the following XML:

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <RetentionValue code="NR" fullName="Normal Retention">Used for normal retention periods commonly expressed as only numbers (i.e. "25" for 25 years).</RetentionValue>
  <RetentionValue code="SR" fullName="Short Retention">Used for short retention periods expressed in terms of months (i.e. "6 months").</RetentionValue>
  <RetentionValue code="AR" fullName="Annual Review">Review annually and retain what is needed, dispose of what is not needed.</RetentionValue>
  <RetentionValue code="PE" fullName="Permanent">Document is to be retained permanently.</RetentionValue>
  <RetentionValue code="EX" fullName="Expiration">Disposal date is calculated from expiration of a contract, loan, or other such instrument.</RetentionValue>
  <RetentionValue code="LI" fullName="Lifetime">Disposal date is calculated from the end-of-life date for a piece of equipment or other asset (e.g. software application).</RetentionValue>
  <RetentionValue code="TE" fullName="Employee Termination">Disposal date is calculated from the date of termination or retirement of an employee.</RetentionValue>
  <RetentionValue code="FR" fullName="Final Resolution">Disposal date is calculated from the date of final resolution for an issue.</RetentionValue>
</root>

In my code, I create an object that has a property RetentionEvent that has a value of one of the two-letter codes above. I want to find the Element with the matching Attribute, then return the fullName to one text field and the value (wordy description) to another text field. So far I have the following:

// Construct a RecordDisposal object by passing it a valid retention code.
// An exception will be thrown if the code is not valid.
_rd = new RecordDisposal(RetentionCode.Text);
// Load up XML file with description of codes.
XDocument rangeValues = XDocument.Load("DisposalRangeValues.xml");
XElement codeValue = rangeValues.Root.Elements().Single(x => (string)x.Attribute("code") == _rd.DisposalList[0].RetentionEvent);
CodeName.Text = codeValue.Attribute("fullName").Value.ToString();
CodeDescription.Text = codeValue.Value.ToString();

The query is a straight mod of this code (I think), however I'm getting a couple of errors I don't understand: "A query body must end with a select clause or a group clause" (I thought that's what the ToList() was for), and "The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'." Unfortunately, I don't understand Linq well enough to troubleshoot this, and "Help on this error" provides nothing useful.

What have I done incorrectly? Also, I'm suspicious that the ToList() function is going to return something that doesn't render well in the ToString() function, but I was waiting to figure out the other stuff before fooling with that. If anyone has a suggestion, please offer it.

EDIT: Modified code to use Single based on recommendation below. Discovered that rangeValues is loading all of <root> as a single node.

EDIT2: Figured it out. Modified code to use rangeValues.Root.Elements() instead. Updated code above to reflect.

2 Answers 2

1

Reformatted slightly, you're code looks like this:

XElement codeValue =
    from fullName
    in rangeValues.Elements()
                  .Where(x => (string)x.Attribute("code") == _rd.DisposalList[0].RetentionEvent)
                  .ToList();
    // select ???

So the ToList() is applied to the in portion of your query, but you're missing a select statement.

Actually, since you're looking to return a single XElement, you probably want something like this, which selects the only element in the collection that matches the clause specified in Single():

XElement codeValue =
    (from fullName in rangeValues.Elements()
     where (string)fullName.Attribute("code") == _rd.DisposalList[0].RetentionEvent
     select fullName).Single();

Alternate syntax (the above is query syntax, the following is called method syntax):

XElement codeValue =
    rangeValues.Elements().Single(x => (string)x.Attribute("code") == _rd.DisposalList[0].RetentionEvent);

If there might not be a match, or could be more than one, you'll want to research SingleOrDefault(), First(), and FirstOrDefault() too.

Sign up to request clarification or add additional context in comments.

2 Comments

I like the Single() method, and as you suspect, don't want what SingleOrDefault() or either of the others would provide. However, I'm getting an exception thrown saying "Sequence contains no matching element" when I know it does. Well, at least I know the value is represented. Whether or not the XML is loading and parsing correctly is another thing.
Got it. Needed to use rangeValues.Root.Elements().
1

Your .ToList() call is correct, but the syntax for the LINQ query is incorrect.

Try this:

List<XElement> fullnames = (from fullName in rangeValues.Elements()
                            where fullName.Attribute("code") == _rd.DisposalList[0].RetentionEvent
                            select fullName).ToList();

1 Comment

This is good, but the Single() method worked better in my case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.