3

Morning,

I would like to know how to write the following SQL statement in LINQ.

 SELECT TOP 6 * FROM Questions
 ORDER BY NEWID()

I would also like to know, how i could bind this to a asp.net repeater control to display the 6 questions.

Many thanks :)

just.another.programmer
  • 8,579
  • 8
  • 51
  • 90
thatuxguy
  • 2,418
  • 7
  • 30
  • 51

6 Answers6

4

The Linq style would be

 Questions.OrderBy(q=>Guid.NewGuid()).Take(6)

then you attach that to a repeater by setting its DataSource property to the above, and calling the DataBind method.

podiluska
  • 50,950
  • 7
  • 98
  • 104
  • cool, thanks, but it only seems to take the first 6 questions – thatuxguy Jul 23 '12 at 08:15
  • 1
    Try adding .ToList() between Questions and OrderBy...? – podiluska Jul 23 '12 at 08:22
  • Sorry, I misread the code when I made that comment. For a moment I thought the `Take()` call came first. But having to call `ToList()` just to make it work isn't much better as it's now a LINQ to objects query. – Jeff Mercado Jul 23 '12 at 09:04
  • This will cause a very inefficient sort because it has to generate a GUID for every record. On very large data sets that could be devestating. Also, a call to ToList will cache the data as a list *before* it applies the Take operator, pulling your entire DB into memory just throw it away! – just.another.programmer Jul 23 '12 at 10:00
  • 1
    @just.another.programmer .... which is exactly what the original SQL would do. The question is not "Is this the best way" or "Is LINQ the best solution" – podiluska Jul 23 '12 at 10:05
  • 1
    @podiluska We always try to address the *intention* of the OP in addition to how he has considered doing it. See http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem on meta about this kind of problem. Otherwise you propogate bad advice on SO and lower the quality of the site! – just.another.programmer Jul 23 '12 at 10:10
4

You would have to be able to invoke the NEWID() function to generate your random guids. To do so, you could take some hints here and first create a pseudo-method mapped to the NEWID() function on your data context.

[System.Data.Linq.Mapping.Function(Name="NEWID", IsComposable=true)] 
public Guid NewId() 
{
    throw new NotImplementedException(); 
}

Once that is set, you could then write your query to use this function:

var query = dc.Questions
    .OrderBy(question => dc.NewId())
    .Take(6);

You can inspect the SQL query generated for this and it should match.

Community
  • 1
  • 1
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
1
Questions.OrderBy(q=>Sql.NewGuid()).Take(6)

This will invoke the NEWID() in SQL statement.

0
(from db in context.Questions
order by Guid.NewGuid()
select db).Take(6);
Alex
  • 87
  • 1
  • 3
  • 8
0

I know answer is already selected, but still I'm adding my way to achieve this. Faced same situation today and tried couple of ways, used questions.OrderBy(q => Guid.NewGuid()).ToList() and couple of more suggestions. Later I thought to add a new field string RandomOrder in view model and assigned Guid.NewGuid().ToString() in loop and then used questions.OrderBy(i => i.RandomOrder).ToList() and this worked great.

I had requirement to shuffle questions if author selected option shuffleAlways while creating assessment. If not then sort on regular sorting order. Here is complete solution:

    private List<AssessmentQuestionsViewModel> LoadAllQuestions(string assessmentId, bool shuffleQuestions)
    {
        List<AssessmentQuestionsViewModel> questions = new List<AssessmentQuestionsViewModel>();

        var items = assessmentQuestionRepository.GetAll().Where(i => i.AssessmentId == assessmentId).ToList();

        foreach (var item in items)
        {
            questions.Add(new AssessmentQuestionsViewModel
            {
                Id = item.Id,
                AssessmentId = item.AssessmentId,
                QuestionText = item.QuestionText,
                HintText = item.HintText,
                QuestionType = item.QuestionType,
                MaxMarks = item.MaxMarks,
                SortOrder = item.SortOrder,
                RandomOrder = Guid.NewGuid().ToString(),
                Answers = LoadAllAnswers(item.Id)
            });
        }

        if (shuffleQuestions)
        {
            questions = questions.OrderBy(i => i.RandomOrder).ToList();
        }
        else
        {
            questions = questions.OrderBy(i => i.SortOrder).ToList();
        }

        return questions;
    }

And this worked like charm. Hope this help others.

Abhimanyu
  • 2,173
  • 2
  • 28
  • 44
-1

I assume you are using ORDER BY NEWID() as a way to select random data from your questions? If so, you should avoid using NEWID() (or it's LINQ equivalent), causes tons a new guid to be generated for every record in your table. On a large dataset, that's devestating.

Instead, see Linq Orderby random ThreadSafe for use in ASP.NET for an optimized solution to random sorts. Then just add a take operator and your set.

Random random = new Random();
int seed = random.Next();

var RandomQuestions = Questions.OrderBy( s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed); 

return RandomQuestions.Take(6);
Community
  • 1
  • 1
just.another.programmer
  • 8,579
  • 8
  • 51
  • 90