我使用EF v5 / 6。我想知道当用户尝试通过UI动态添加搜索过滤器时,如何动态构建where子句。
现在最终用户将通过上述UI添加尽可能多的过滤器并尝试搜索数据库。
我尝试搜索谷歌的一些解决方案,因此我可以添加多个搜索到我的数据库表与EF,我发现很少。这里有几个链接
https://stackoverflow.com/a/5595591/6188148
https://stackoverflow.com/a/24824290/6188148
以上指南对我来说很难理解。所以我看起来很容易。
这个链接接近我的要求 https://stackoverflow.com/a/16646241/6188148但我仍然不知道如何自定义他们的代码以用于我的场景。
所以我的请求任何人都可以提供一些帮助我完成工作的小样本代码。
答案 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));
}