有没有办法可以推迟LINQ语句中的.Where子句?

时间:2016-09-30 09:15:51

标签: c# .net linq

我这里有这个代码。我认为这不是非常有效,我想知道是否有一种方法可以让我这样做。所以.Where子句在开头或结尾添加而不是重复三次。如果有人能给我任何建议并告诉我如何做到这一点,我将不胜感激:

      List<Phrase> phrases;

        switch (options.PhraseTypeSelectId)
        {
            case 0:
                phrases = await db.Phrases
                .Where(w => ((w.CategoryId == options.CategorySelectId || options.CategorySelectId == null) ||
                             (w.ChapterId == options.ChapterSelectId || options.ChapterSelectId == null)))
                .Where(w => (w.EnglishAscii >= es1 && w.EnglishAscii <= ee1) || (w.EnglishAscii >= es2 && w.EnglishAscii <= ee2))
                .Where(w => (w.RomajiAscii >= rs1 && w.RomajiAscii <= re1) || (w.RomajiAscii >= rs2 && w.RomajiAscii <= ee2))
                .Where(w => (options.CreatedBy == 0 || w.CreatedBy == options.CreatedBy))
                .Where(w => (options.ModifiedBy == 0 || w.ModifiedBy == options.ModifiedBy))
                .Where(w => (options.JLPT == 0 || w.JLPT == options.JLPT))
                .AsNoTracking()
                .ToListAsync();
                return Ok(phrases);
            case 1:
                phrases = await db.Phrases
                .Where(w => ((w.CategoryId == options.CategorySelectId || options.CategorySelectId == null)))
                .Where(w => (w.EnglishAscii >= es1 && w.EnglishAscii <= ee1) || (w.EnglishAscii >= es2 && w.EnglishAscii <= ee2))
                .Where(w => (w.RomajiAscii >= rs1 && w.RomajiAscii <= re1) || (w.RomajiAscii >= rs2 && w.RomajiAscii <= ee2))
                .Where(w => (options.CreatedBy == 0 || w.CreatedBy == options.CreatedBy))
                .Where(w => (options.ModifiedBy == 0 || w.ModifiedBy == options.ModifiedBy))
                .Where(w => (options.JLPT == 0 || w.JLPT == options.JLPT))
                .AsNoTracking()
                .ToListAsync();
                return Ok(phrases);
            case 2:
                phrases = await db.Phrases
                .Where(w => ((w.ChapterId == options.ChapterSelectId || options.ChapterSelectId == null)))
                .Where(w => (w.EnglishAscii >= es1 && w.EnglishAscii <= ee1) || (w.EnglishAscii >= es2 && w.EnglishAscii <= ee2))
                .Where(w => (w.RomajiAscii >= rs1 && w.RomajiAscii <= re1) || (w.RomajiAscii >= rs2 && w.RomajiAscii <= ee2))
                .Where(w => (options.CreatedBy == 0 || w.CreatedBy == options.CreatedBy))
                .Where(w => (options.ModifiedBy == 0 || w.ModifiedBy == options.ModifiedBy))
                .Where(w => (options.JLPT == 0 || w.JLPT == options.JLPT))
                .AsNoTracking()
                .ToListAsync();
                return Ok(phrases);
        }
        return BadRequest();

我想在没有这个的情况下包含switch语句,它与与PhraseTypeSelectId相关的.Where子句变得非常混乱

5 个答案:

答案 0 :(得分:3)

由于我无法编译这个例子,我不能100%确定这会编译,但你应该得到它的要点。

您可以为不同的PhraseTypeSelecteId创建特定的表达式,稍后在.Where()方法中使用它。

Expression<Func<Phrase, bool>> phraseTypePredicate = null;

switch (options.PhraseTypeSelectId)
{
    case 0:
        phraseTypePredicate =
            w => ((w.CategoryId == options.CategorySelectId || options.CategorySelectId == null) ||
                  (w.ChapterId == options.ChapterSelectId || options.ChapterSelectId == null));
        break;
    case 1:
        phraseTypePredicate =
            w => ((w.CategoryId == options.CategorySelectId || options.CategorySelectId == null));
        break;
    case 2:
        phraseTypePredicate =
            w => ((w.ChapterId == options.ChapterSelectId || options.ChapterSelectId == null));
        break;
}

if (phraseTypePredicate != null)
{
    List<Phrase> phrases = phrases = await db.Phrases
        .Where(phraseTypePredicate)
        .Where(w => (w.EnglishAscii >= es1 && w.EnglishAscii <= ee1) || (w.EnglishAscii >= es2 && w.EnglishAscii <= ee2))
        .Where(w => (w.RomajiAscii >= rs1 && w.RomajiAscii <= re1) || (w.RomajiAscii >= rs2 && w.RomajiAscii <= ee2))
        .Where(w => (options.CreatedBy == 0 || w.CreatedBy == options.CreatedBy))
        .Where(w => (options.ModifiedBy == 0 || w.ModifiedBy == options.ModifiedBy))
        .Where(w => (options.JLPT == 0 || w.JLPT == options.JLPT))
        .AsNoTracking()
        .ToListAsync();
    return Ok(phrases);
}

return BadRequest();

答案 1 :(得分:3)

您可以使用扩展方法.AsQueryable()这将返回linq语句的前一部分作为可查询表达式,如果需要允许进一步操作和转换。

例如

var query =db.Phrases.where(phraseTypePredicate).AsQueryable();

switch(options.PhraseTypeSelectId)
{
   case 1: 
     query = query.where(case1Predicate);
     break;
   case 2: 
     query = query.where(case2Predicate);
     break;
   ... 
}

var results = query.ToList(); // or someother way of enumerating the expression

答案 2 :(得分:2)

如果我理解正确,问题是如何构建动态过滤器。为此,您可以从基本的IQueryable<T>开始,并应用基于参数的条件Where,如下所示:

var query = db.Phrases.AsQueryable();

bool categoryFilter = options.CategorySelectId != null && options.PhraseTypeSelectId != 2;
bool chapterFilter = options.ChapterSelectId != null && options.PhraseTypeSelectId != 1;
if (categoryFilter && chapterFilter) query = query
    .Where(w => w.CategoryId == options.CategorySelectId || w.ChapterId == options.ChapterSelectId);
else if (categoryFilter) query = query
    .Where(w => w.CategoryId == options.CategorySelectId);
else if (chapterFilter) query = query
   .Where(w => w.ChapterId == options.ChapterSelectId);

query = query
    .Where(w => (w.EnglishAscii >= es1 && w.EnglishAscii <= ee1) || (w.EnglishAscii >= es2 && w.EnglishAscii <= ee2))
    .Where(w => (w.RomajiAscii >= rs1 && w.RomajiAscii <= re1) || (w.RomajiAscii >= rs2 && w.RomajiAscii <= ee2));

if (options.CreatedBy != 0) query = query
    .Where(w => w.CreatedBy == options.CreatedBy);

if (options.ModifiedBy != 0)
    query = query.Where(w => w.ModifiedBy == options.ModifiedBy);

if (options.JLPT != 0)
    query = query.Where(w => w.JLPT == options.JLPT);

var phrases = await query
    .AsNoTracking()
    .ToListAsync();

return Ok(phrases);

答案 3 :(得分:1)

IQueryableIEnumerable上的LINQ查询在您实际迭代结果之前不会被评估 - 通过循环结束,或调用ToList()或类似的结果。

因此,您可以分阶段构建事物,例如:

var exp = dbcontext.Logs.Where(x => x.Code == 4);

它不会执行数据库上的任何代码。然后你可以稍后说:

var logs = await exp.Where(x => x.Module == "AUTH").ToListAsync();
由于ToListAsync()

执行查询。

这方面的巨大优势是能够使用合理的方法分阶段构建查询(而不是切割SQL的字符串),并相信实体框架或您正在使用的任何LINQ提供程序将把它变成合理的查询(大部分时间都是这样做的。)

因此,对于你的大量例子,我不打算在这里重写它,但你肯定可以用这种形式做一些事情:

var query = await db.Phrases;
// common where clauses

switch (options.PhraseTypeSelectId) {
    case 0:
        query = query.Where(w => /* appropriate restriction for 0 */);
        break;
    // other cases
}

// more conditional where clauses perhaps

// projection clauses like Select()

// finally, execute the query
List<Phrases> phrases = await query.ToListAsync();

答案 4 :(得分:1)

这也应该有用(细节可能是错误的,只是概念):

// common filters here:
var filtered = db.Phrases
       .Where(w => (w.EnglishAscii >= es1 && w.EnglishAscii <= ee1) || (w.EnglishAscii >= es2 && w.EnglishAscii <= ee2))
       .Where(w => (w.RomajiAscii >= rs1 && w.RomajiAscii <= re1) || (w.RomajiAscii >= rs2 && w.RomajiAscii <= ee2))
       .Where(w => (options.CreatedBy == 0 || w.CreatedBy == options.CreatedBy))
       .Where(w => (options.ModifiedBy == 0 || w.ModifiedBy == options.ModifiedBy))
       .Where(w => (options.JLPT == 0 || w.JLPT == options.JLPT));

    // specific filters
    switch (options.PhraseTypeSelectId)
    {
        case 0:
            filtered = filtered 
                .Where(w => ((w.CategoryId == options.CategorySelectId || options.CategorySelectId == null) ||
                         (w.ChapterId == options.ChapterSelectId || options.ChapterSelectId == null)))
             break;
        case 1:
            filtered = filtered 
                .Where(w => ((w.CategoryId == options.CategorySelectId || options.CategorySelectId == null)))
             break;
        case 2:
            // ???
             break;
        default: 
           return BadRequest();
    }
    var phrases = async filtered
        .AsNoTracking()
        .ToListAsync();
    return Ok(phrases);