实体框架6:针对DB动态构建搜索子句

时间:2017-06-21 13:17:17

标签: c# entity-framework

我使用EF v5 / 6。我想知道当用户尝试通过UI动态添加搜索过滤器时,如何动态构建where子句。

首先查看用户界面 enter image description here

现在最终用户将通过上述UI添加尽可能多的过滤器并尝试搜索数据库。

我尝试搜索谷歌的一些解决方案,因此我可以添加多个搜索到我的数据库表与EF,我发现很少。这里有几个链接

https://stackoverflow.com/a/5595591/6188148

https://stackoverflow.com/a/24824290/6188148

以上指南对我来说很难理解。所以我看起来很容易。

这个链接接近我的要求 https://stackoverflow.com/a/16646241/6188148但我仍然不知道如何自定义他们的代码以用于我的场景。

所以我的请求任何人都可以提供一些帮助我完成工作的小样本代码。

2 个答案:

答案 0 :(得分:4)

我假设您能够从GUI中的每一行构建一个子条件。例如,第一行与

类似
string userInputName = "john";
Expression<Func<Person, bool>> condition1 = person => person.Name.Contains(userInputName);

可以用作

var selection = db.Persons.Where(condition1).ToList();

对于多个子条件,and只是Where条件的串联:

var conditions = new List<Expression<Func<Person, bool>>>() { condition1, condition2, ... };

IQueryable<Person> query = db.Persons;
foreach (var condition in conditions)
{
    query = query.Where(condition);
}
var selection = query.ToList();

棘手的部分来自or条件。假设您已将条件规范化为备选and条件组(disjunctive normal form),则可获得多组有效结果。为了简单起见,我在这里将其保留为2组,但它可以与and条件相同的方式进行推广:

or可以通过子查询的Union操作来表示。

var conditions1 = new List<Expression<Func<Person, bool>>>() { condition1, condition2, ... };
IQueryable<Person> query1 = // see above construction logic for 'and' conditions
var conditions2 = new List<Expression<Func<Person, bool>>>() { condition5, condition6, ... };
IQueryable<Person> query2 = // see above construction logic for 'and' conditions

IQueryable<Person> query = query1.Union(query2);
var selection = query.ToList();

最后几句话:考虑使用一些已建立的过滤器/搜索框架。这种方法可能既不是最漂亮的也不是最快的。

根据要求,提供一些内存数据的小例子。请注意,这不是100%相当于Linq-to-entities。例如,字符串比较将以不同的方式处理大小写,并且可能不允许SQL的每种条件。

public enum TypeOfContact
{
    Unknown,
    Email
}
public class Person
{
    public string Name { get; set; }

    public DateTime Birth { get; set; }

    public TypeOfContact ContactType { get; set; }

    public string ContactValue { get; set; }
}

public class Program
{
    static void Main(string[] args)
    {
        // test data to simulate a database table
        var Persons = new List<Person>
        {
            // + All conditions met
            new Person { Name = "john doe", Birth = new DateTime(2011, 1, 1), ContactType = TypeOfContact.Email, ContactValue = "john.doe@email.com" },
            // - Not in result
            new Person { Name = "danny doe", Birth = new DateTime(2012, 1, 1), ContactType = TypeOfContact.Email, ContactValue = "johndoe@hotmail.com" },
            // + Name contains john
            new Person { Name = "john doe", Birth = new DateTime(2013, 1, 1), ContactType = TypeOfContact.Unknown, ContactValue = "" },
            // + Birth, ContactType and ContactValue correct
            new Person { Name = "justin", Birth = new DateTime(2014, 1, 1), ContactType = TypeOfContact.Email, ContactValue = "slenderman@email.com" },
            // - Not in result because Name and Birth are wrong
            new Person { Name = "jonny", Birth = new DateTime(1979, 1, 1), ContactType = TypeOfContact.Email, ContactValue = "you-know-who@email.com" },
            // - Not in result
            new Person { Name = "jenny doe", Birth = new DateTime(2016, 1, 1), ContactType = TypeOfContact.Unknown, ContactValue = "" },
        }.AsQueryable();

        // single-line-conditions
        Expression<Func<Person, bool>> c1 = p => p.Name.Contains("john");
        Expression<Func<Person, bool>> c2 = p => p.Birth.Date >= new DateTime(1980, 1, 1);
        Expression<Func<Person, bool>> c3 = p => p.ContactType == TypeOfContact.Email;
        Expression<Func<Person, bool>> c4 = p => p.ContactValue.EndsWith("@email.com");

        // DNF groups: outer list = or; inner list = and
        // c1 or (c2 and c3 and c4)
        var conditionList = new List<List<Expression<Func<Person, bool>>>>
        {
            new List<Expression<Func<Person, bool>>>
            {
                c1,
            },
            new List<Expression<Func<Person, bool>>>
            {
                c2,
                c3,
                c4,
            },
        };

        var andSubResults = new List<IQueryable<Person>>();
        foreach (var andQueries in conditionList)
        {
            var subQuery = Persons;
            foreach (var andQuery in andQueries)
            {
                subQuery = subQuery.Where(andQuery);
            }
            andSubResults.Add(subQuery);
        }
        var query = andSubResults.FirstOrDefault();
        foreach (var subResult in andSubResults.Skip(1))
        {
            query = query.Union(subResult);
        }
        var selection = query.ToList();
        // just check the result in debugger
    }
}

答案 1 :(得分:0)

@ grek40的回答为我提供了此方法所需的线索。我只有几个anded条件,不确定它是否适用于ored条件。

using (var theDb = new project_dbEntities())
{
    IQueryable<TheEntityClass> query = theDbContext.TheEntityClass;

    if (!showInactive)
    {
        query = query.Where(x => x.active);
    }

    if (selectedYearId != years.ALL_YEARS)
    {
        var yearToFilter = years.GetById(selectedYearId);
        query = query.Where(x => x.first_effective_date >= yearToFilter.start &&
                                 x.first_effective_date <= yearToFilter.end);
    }

    var entities = query
        .Include(nameof(TheChildEntity))
        .OrderBy(x => x.name)
        .ToList();

    return View(new IndexViewModel(entities));
}