2017-08-11 141 views
2

,我需要選擇每個類別隨機抽題選擇與where子句中的LINQ

private int[] categoryId = {1, 2, 3, 4, 5, ...}; 
private int[] questionsPerCategory = {3, 1, 6, 11, 7, ...}; 

之前LINQ我用

SELECT TOP (@questionsPerCategory) * From Questions WHERE CategoriesID = @categoryId AND 
InTest ='1' ORDER BY NEWID() 

這也是不正確實現它隨機出題,因爲我不得不爲每個categoryId調用它。

如何在單個查詢中使用linq獲得期望的結果? 我需要的是取

  • 3個隨機的問題,用的categoryId = 1和INTEST =真,
  • 1隨機問題,具有的categoryId = 2和INTEST =真,
  • 6隨機問題,具有的categoryId = 3和INTEST =真

等..

+1

'問題'表是怎麼樣的?你是什​​麼意思的「隨機」? –

+0

@JakubDąbek在其他列中有CategoriesID和InTest – OrElse

回答

2

由於Guid.NewGuid不LINQ支持到SQL,首先你需要使用從接受答案的伎倆通過添加以下到你的上下文類去Random row from Linq to Sql訪問NEWID功能:

partial class YourDataContext { 
    [Function(Name="NEWID", IsComposable=true)] 
    public Guid Random() 
    { // to prove not used by our C# code... 
     throw new NotImplementedException(); 
    } 
} 

然後單CategoryID和問題數的查詢是:

var query = db.Questions 
    .Where(e => e.CategoriesID == categoryId[i] && e.InTest) 
    .OrderBy(e => db.Random()) 
    .Take(questionsPerCategory[i]) 

爲了得到期望的結果對所有類別/問題數對,你可以建立一個UNION ALL SQL查詢使用上述單查詢Concati = 0..N這樣的:

var query = categoryId.Zip(questionsPerCategory, 
    (catId, questions) => db.Questions 
     .Where(q => q.CategoriesID == catId && q.InTest) 
     .OrderBy(q => db.Random()) 
     .Take(questions) 
    ).Aggregate(Queryable.Concat) 
    .ToList(); 

這應該產生預期的結果與單個SQL查詢。當然,如果categoryId的計數相對較小,它是適用的。

1

我認爲你正在尋找採取()方法。 您還應該將參數傳遞給具有類別ID的方法以及您想要接收的問題數量。從數組中傳遞這些參數。

private IQuerable<Question> Method(int Id, int questionsCount) 
{  
return Questions.Where(c=>c.CategoriesId==Id && c.InTest==1).Take(questionsCount).OrderBy(c=>c.NewId); 
} 
+0

運算符'=='不能應用於類型'int'和'int []'的操作數 – OrElse

+0

查看更新回答 – Crekate

+0

檢查它。它仍然抱怨。無法從'int'轉換爲'System.Func ' – OrElse

0

一個常見的方法是按Guid.NewGuid()的順序排列,以便延伸上面的Crekate的答案。

.OrderBy(c=>Guid.NewGuid()); 
+0

感謝Daniel,問題是無法從'int'轉換爲'System.Func 在這一個c => categoryId.Any(c.CategoriesId) – OrElse

2

也許你想要這樣的事情,你先做一個組,然後從每個類別中選擇你想要的數量。

編輯:正如Enigmativity在評論中指出的那樣,Guid.NewGuid()不應該用於唯一性的隨機性。要產生隨機性,你應該諮詢this StackOverflow post

Demo

using System; 
using System.Linq; 
using System.Collections.Generic; 

public class Program 
{ 
    private static int[] categoryIds = new int[] {1, 2, 3, 4, 5}; 
    private static int[] questionsPerCategory = {3, 1, 6, 11, 7}; 
    //Part of demo 
    private static IEnumerable<QuestionVM> Questions = Enumerable.Range(0,100).Select(x=> new QuestionVM { Question = $"Question - {x}", CategoryId = (x % 5) + 1}); 


    public static void Main() 
    { 
     var questions = Questions.Where(x=> x.InTest).GroupBy(x=> x.CategoryId).SelectMany(x=> x.OrderBy(y=> Guid.NewGuid()).Take(GetQuestionTake(x.Key))); 
     foreach(var question in questions) 
      Console.WriteLine($"{question.Question} - CategoryId: {question.CategoryId}"); 
    } 

    ///Finds out how many questions it should take by doing a search and then picking the element in the same position 
    private static int GetQuestionTake(int categoryId) 
    { 
     int element = categoryIds.Select((x, i) => new { i, x }).FirstOrDefault(x => x.x == categoryId).i; 
     return questionsPerCategory.ElementAtOrDefault(element); 
    } 
} 


//Part of demo 
public class QuestionVM 
{ 
    public string Question {get;set;} 
    public int CategoryId {get;set;}  
    public bool InTest {get;set;} = true; 
} 
+0

它按預期工作,但不幸的是,如果我添加.ToList()之前.SelectMany我將最終得到所有問題,然後調用GetQuestionTake方法。這是真的還是我錯過了什麼? – OrElse

+0

如果你做一個.ToList()它將取出所有東西。如果你不喜歡GetQuestionTake方法,我會建議將categoryIds和questionsPerCategory轉換爲另一種類型,如字典,GetQuestionTake方法爲每個類別ID調用。 – Shoejep

+0

不幸的是,如果我不首先調用.ToList(),GetQuestionTake將不會被轉換爲SQL。通過改變字典,我會獲得什麼?我不明白,實際上 – OrElse