开发者

Use Optional OR Clause in Linq.Table.Where()

Is there a way to make the ProjectID check below part of an optional block? I'm a recent .Net convert from Java EE and I'm looking for something similar to the Hibernate Criteria API. I'd like to simplify the block below and only have to call Where() once. I'm also not sure of the performance implications of doing a Where() with lambdas as I just started working with .Net a week ago.

public IQueryable<Project> FindByIdOrDescription(string search)
    {
        int projectID;
        bool isID = int.TryParse(search,开发者_如何学运维 out projectID);

        IQueryable<Project> projects;

        if (isID)
        {
            projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search) || p.ProjectID == projectID);
        }
        else
        {
            projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search));
        }

        return projects;
    }


If you're looking for optionally adding AND xyz, you could simply chain Where calls:

var projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search));
if (isID)
    projects = projects.Where(p => p.ProjectID == projectID);

Unfortunately things are not so easy when you'd like to do an OR xyz. For this to work, you'll need to build a predicate expression by hand. It's not pretty. One way to do this is

Expression<Func<Project, bool>> predicate = p => p.ProjectDescription.Contains(search);
if (isID)
{
    ParameterExpression param = expr.Body.Parameters[0];
    predicate = Expression.Lambda<Func<Project, bool>>(
        Expression.Or(
            expr.Body,
            Expression.Equal(
                Expression.Property(param, "ProjectID"),
                Expression.Constant(projectID))),
        param);
}
var projects = dataContext.Projects.Where(predicate);

Note that adding a condition to the existing predicate is a lot more work than creating the initial expression, because we need to completely rebuild the expression. (A predicate must always use a single parameter; declaring two expressions using lambda syntax will create two separate parameter expression objects, one for each predicate.) Note that the C# compiler does roughly the same behind the scenes when you use lambda syntax for the initial predicate.

Note that this might look familiar if you're used to the criteria API for Hibernate, just a little more verbose.


Note, however, that some LINQ implementations are pretty smart, so the following may also work:

var projects = dataContext.Projects.Where(
    p => p.ProjectDescription.Contains(search)
      || (isID && p.ProjectID == projectID));

YMMV, though, so do check the generated SQL.


The query isn't parsed and executed until the first time you actually access the elements of the IQueryable. Therefore, no matter how many where's you keep appending (which you're not even doing here anyway) you don't need to worry about hitting the DB too many times.


Delegates / expressions can be "chained", like this (untested pseudo-code):

Expression<Predicate<Project>> predicate = p => p.ProjectDescription.Contains(search);
if ( isID ) 
    predicate = p => predicate(p) || p.ProjectID == projectId;

return dataContext.Where(predicate);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜