Best linq questions in February 2012

ReSharper gives an "@" prefix to a variable name in a lambda expression

13 votes

When using ReSharper it automatically adds an @, why?

public static string RemoveDiacritics(this string input)
{
    if (string.IsNullOrEmpty(input)) return input;
    var normalizedString = input.Normalize(NormalizationForm.FormD);
    var stringBuilder = new StringBuilder();
    foreach (var value in normalizedString.Select(value => 
        new {value, unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(value)})
            .Where(@t => @t.unicodeCategory != UnicodeCategory.NonSpacingMark)
            .Select(@t => @t.value)) stringBuilder.Append(value);
    return (stringBuilder.ToString().Normalize(NormalizationForm.FormC));
}

The @ symbol allows you to use a reserved keyword for a variable name. Such as @class. I'd assume Resharper does this to be safe.

In this case, it is not needed and it doesn't have any effect.

How to implement SkipWhile with Linq to Sql without first loading the whole list into memory?

8 votes

I need to order the articles stored in a database by descending publication date and then take the first 20 records after the article with Id == 100.

This is what I would like to do with Linq:

IQueryable<Article> articles = 
    db.Articles
    .OrderByDescending(a => a.PublicationDate)
    .SkipWhile(a => a.Id != 100)
    .Take(20);

However, this generates a NotSupportedException because SkipWhile is not supported in Linq to Sql (see here).

A possible solution is to execute the query and then apply SkipWhile using Linq to Object:

IEnumerable<ArticleDescriptor> articles = 
    db.Articles
    .OrderByDescending(a => a.PublicationDate)
    .ToList()
    .SkipWhile(a => a.Article.Id != 100)
    .Take(20);

But this means I need to load the whole ordered list into memory first and then take 20 articles after the one with Id == 100.

Is there a way to avoid this huge memory consumption?

More in general, what is the best way to achieve this in SQL?

If, as I'm guessing from the column name, PublicationDate doesn't change, you can do this in two separate queries:

  • Establish the PublicationDate of the Article with Id == 100
  • Retrieve the 20 articles from that date onwards

Something like:

var thresholdDate = db.Articles.Single(a => a.Id == 100).PublicationDate;
var articles = 
    db.Articles
    .Where(a => a.PublicationDate <= thresholdDate)
    .OrderByDescending(a => a.PublicationDate)
    .Take(20);

It might even be that LINQ to SQL can translate this:

var articles = 
    db.Articles
    .Where(a => a.PublicationDate 
             <= db.Articles.Single(aa => aa.Id == 100).PublicationDate)
    .OrderByDescending(a => a.PublicationDate)
    .Take(20);

but that may be too complex for it. Try it and see.

Linq query based on attribute

4 votes

Ran into another challenge. I looked through some of the questions that I found here, but I can't seem to piece together what I need.

OK I have a XML file:

<Output id="1">
    <path rename="Off" name="pattern-1">d:\temp</path>
  </Output>

  <Output id="2">
      <path isRename="False" name="pattern-1" >d:\temp\out2</path>
      <path isRename="True"  name="pattern-1" >d:\temp\out3</path>
      <path isRename="False" name="pattern-1">d:\temp\out4</path>
  </Output>

What I need to do is find the <Output> tag based on the id attribute . Then I need to loop through all of the <path> tags and get the attribute and path value. I tried a few thing based on a previous question I had asked but I couldn't get it to work

var results = from c in rootElement.Elements("Output") 
              where (string)c.Attribute("Id") == "2" select c;

foreach (var path in rootElement.Elements("Output").Elements("path"))
{
    string p  = path.Value;
}

Your first line doesn't do anything if you don't actually use the results.

foreach (var outputElement in rootElement.Elements("Output")
                                         .Where(e => (string)e.Attribute("id") == "1"))
{
    foreach (var pathElement in outputElement.Elements("path"))
    {
        // ...
    }
}

If your id attribute is guaranteed to be unique (which it should), you can get rid of the first foreach and just get the individual <Output> directly:

var outputElement = rootElement.Elements("Output")
                               .FirstOrDefault(e => (string)e.Attribute("id") == "1"));