构建动态LINQ查询的最佳方法

7

你好,我正在寻找编写动态LINQ查询的最佳方法。

我有一个类似于以下的函数:

public IQueryable<Student> FindByAllStudents(int? id, string Name, int? CourseID, bool? IsActive) // like this way, all field values are passed
    {    
        // code for compairision
        return db.Student;
    }

我们也可以编写db.Students.where(predicate)这样的查询,或者像下面这样的查询:
var students = from s in db.students where s.Name.Contains(Name)
                s.ID.Equals(id)
                //and so on....

如果我不传递ID(即Null),这种方法是否有效?对于所有数据类型来说,这是正确的方式吗?

关键在于函数可以将所有空值作为参数,以实现与select * from语句的等效性。

有人能帮我构建最佳查询和样例代码吗?

1个回答

21

好的,你想要什么并不是完全清楚,但如果你想要只添加非空参数的where子句,你可以这样做:

public IQueryable<Student> FindByAllStudents
    (int? id, string name, int? courseID, bool? isActive)
{    
    IQueryable<Student> query = db.Student;
    if (id != null)
    {
        query = query.Where(student => student.ID == id.Value);
    }
    if (name != null)
    {
        query = query.Where(student => student.Name.Contains(name));
    }
    if (courseID != null)
    {
        query = query.Where(student => student.CourseID == courseID.Value);
    }
    if (isActive != null)
    {
        query = query.Where(student => student.IsActive == isActive.Value);
    }
    return query;
}

我没有尝试过这个,而且 LINQ to SQL 可能会被查找可空值类型的代码所困惑。你可能需要编写像这样的代码:

    if (courseID != null)
    {
        int queryCourseID = courseID.Value;
        query = query.Where(student => student.CourseID == queryCourseID);
    }

不过,先尝试简单的形式是值得的 :)

当然,所有这些都有点让人恼火。一个有用的扩展方法可以使生活更简洁:

public static IQueryable<TSource> OptionalWhere<TSource, TParameter>
    (IQueryable<TSource> source,
     TParameter? parameter, 
     Func<TParameter, Expression<Func<TSource,bool>>> whereClause)
    where TParameter : struct
{
    IQueryable<TSource> ret = source;
    if (parameter != null)
    {
        ret = ret.Where(whereClause(parameter.Value));
    }
    return ret;
}

那么你将像这样使用它:

public IQueryable<Student> FindByAllStudents
    (int? id, string name, int? courseID, bool? isActive)
{    
    IQueryable<Student> query = db.Student
        .OptionalWhere(id, x => (student => student.ID == x))
        .OptionalWhere(courseID, x => (student => student.CourseID == x))
        .OptionalWhere(isActive, x => (student => student.IsActive == x));
    if (name != null)
    {
        query = query.Where(student => student.Name.Contains(name));
    }
    return query;
}

如果你对使用高阶函数不太熟悉,那么像这样使用可能会让人感到困惑。因此,如果你并没有进行很多这样的查询,你可能会选择坚持使用更长但更简单的代码。


谢谢!对于第一个解决方案,我们也可以使用 if(id.HasValue) { 然后使用 id.Value }。 - Vikas
你能指导我如何使用全文搜索吗? - Vikas
使用 HasValue 相当于使用 != null。我不知道如何在 LINQ 中使用全文搜索,恐怕不行。 - Jon Skeet
喜欢这个扩展方法Jon!有关Linq +全文搜索的讨论,请参见:https://dev59.com/lnVC5IYBdhLWcg3wpS7g - Pure.Krome
如果我可以稍微评论一下,我在使用字符串时遇到了问题,因为字符串已经是可空的(据我所知,它是引用类型而不是结构体——如果我理解正确的话)。刚才又看了一下代码,我发现你单独查询了名称——可能是由于这个限制? - Remotec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接