tags:

views:

2896

answers:

2

How can I project the row number onto the linq query result set.

Instead of say:

field1, field2, field3

field1, field2, field3

I would like:

1, field1, field2, field3

2, field1, field2, field3

Here is my attempt at this:

public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
    Guid guid = new Guid(gameId);
    using (PPGEntities entities = new PPGEntities())
    {
        int i = 1;
        var query = from s in entities.Scores
                    where s.Game.Id == guid
                    orderby s.PlayerScore descending
                    select new ScoreWithRank()
                    {
                        Rank=i++,
                        PlayerName = s.PlayerName,
                        PlayerScore = s.PlayerScore
                    };
        return query.ToList<ScoreWithRank>();
    }
}

Unfortunately, the "Rank=i++" line throws the following compile-time exception:

"An expression tree may not contain an assignment operator"

+9  A: 

Well, the easiest way would be to do it at the client side rather than the database side, and use the overload of Select which provides an index as well:

public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
    Guid guid = new Guid(gameId);
    using (PPGEntities entities = new PPGEntities())
    {
        var query = from s in entities.Scores
                    where s.Game.Id == guid
                    orderby s.PlayerScore descending
                    select new
                    {
                        PlayerName = s.PlayerName,
                        PlayerScore = s.PlayerScore
                    };

        return query.AsEnumerable() // Client-side from here on
                    .Select((player, index) => new ScoreWithRank()
                            {
                                PlayerName = player.PlayerName,
                                PlayerScore = player.PlayerScore,
                                Rank = index + 1;
                            }
                    .ToList();

    }
}
Jon Skeet
A: 

Ok, that did the trick. Thanks.

Here is my final code...

Server:

public List<Score> GetHighScores(string gameId, int count)
{
    Guid guid = new Guid(gameId);
    using (PPGEntities entities = new PPGEntities())
    {
        var query = from s in entities.Scores
                    where s.Game.Id == guid
                    orderby s.PlayerScore descending
                    select s;
        return query.ToList<Score>();
    }                                                                      
}

Client:

void hsc_LoadHighScoreCompleted(object sender, GetHighScoreCompletedEventArgs e)
{
    ObservableCollection<Score> list = e.Result;

    _listBox.ItemsSource = list.Select((player, index) => new ScoreWithRank()
                            {
                                PlayerName = player.PlayerName,
                                PlayerScore = player.PlayerScore,
                                Rank = index+=1
                            }).ToList();
}
Jeff Weber
Do you really need GetHighScores() to return a List<Score> instead of an IEnumerable<Score>? If you're going to convert it into a list, you might as well only do it once.
Jon Skeet
@Jon: He could call AsEnumerable instead but...The AsEnumerable method has no effect other than to change the compile-time type of source.http://msdn.microsoft.com/en-us/library/bb335435.aspx - in other words, it won't bring the objects into memory. If he wants control over that, ToList is good
David B
Yes, but only *if* he needs to do it at that point. If he doesn't need to there's no point in copying all the data twice. Hence the question nature of my cooment :) In fact even AsEnumerable isn't needed of course - if the GetHighScores method is declared to return IEnumerable<Score> that'll do it.
Jon Skeet