我对MVC世界很陌生,但多年来我一直在做 asp.net。我用 mvc 开始了这个项目,所以我可以习惯它。我一直在苦苦挣扎 linq 生成的一些过于复杂的 sql 语句,我认为我做错了什么。也许它做对了,如果不从头开始编写 sql 语句,我就无法分辨。
好的,目标是我得到了这个身份数据库,我添加了一些字段,并在地址和电话号码等其他一些表中链接。
我正在供应商搜索屏幕上工作,并希望使用 linq 获取数据。一些信息在下面的示例中 table1 的下拉列表中,并且有一个文本搜索选项,允许对主 aspnet 用户和其他一些表上的多个字段进行"OR"搜索。
我希望有人帮助我在 linq 上获得简化版本。或者我应该开始编写我的 sql 并以某种方式在这里使用它(不知道如何在 mvc 中做到这一点,但应该非常相似
var VendorRole = await db.Roles.FirstAsync(r => r.Name == "vendor");
var results = db.Users.Include(u => u.table1).
Include(u => u.table2).Include(u => u.table3).
Include(u => u.table4).Include(u => u.table5).
Where(u => u.Roles.Any(s => s.RoleId == VendorRole.Id));
if (id1.HasValue)
{
results.Where(m => m.table1.First(a => a.id1 == id1) != null);
}
if (id2.HasValue)
{
results.Where(m => m.table1.First(a => a.id2 == id2) != null);
}
if (id3.HasValue)
{
results.Where(m => m.table1.First(a => a.id3 == id3) != null);
}
IEnumerable<string> searchTerms = SearchString.Split(' ')
.Select(x => x.Trim());
foreach (string term in searchTerms)
{
if (!string.IsNullOrWhiteSpace(term))
{
string captured = term.ToUpper();
results = results.Where(t => (t.field1.ToUpper().Contains(captured) ||
t.field2.ToUpper().Contains(captured) ||
t.field3.ToUpper().Contains(captured) ||
(t.field2 + " " + t.field3).ToUpper().Contains(captured) ||
t.field4.ToUpper().Contains(captured) ||
t.field5.ToUpper().Contains(captured) ||
t.table2.Any(m => (m.field6 + " " + m.field7).ToUpper().Contains(captured)) != null));
}
}
ViewBag.count = results.Count();
if ((page -1) * 10 > ViewBag.count)
{
page = (int)Math.Floor((double)ViewBag.count / 10);
}
ViewBag.page = page;
return PartialView(await results.OrderBy(m => m.field1 + " " + m.field2 + " " + m.field3).Skip((page - 1) * 10).Take(10).ToListAsync());
感谢您花时间提供帮助。
现在的外观是,每个Where
子句都使用集合上的AND
进行匹配,而不是OR
。
如果您确实想在所有情况下使用 OR
,以下内容应该很有用。
正如我在另一个答案中提到的,使用 Linq
构建复杂查询谓词的正确方法是使用 PredicateBuilder
。完成此操作后,您可以将代码重构为两部分:
- 构建
Where
子句的谓词和 - 查询数据
代码如下所示:
private Expression<Func<User, bool>> BuildIdQuery(
Expression<Func<User, bool>> predicate,
int? id,
Func<User, int> propertySelector)
{
if(id.HasValue)
{
// First(...) with throw an exception if there are no items
// matching the predicate; Any() is the proper way to do it.
return predicate.Or(m => m.Any(a => propertySelector(a) == id);
}
return predicate;
}
Expression<Func<User, bool>> BuildSearchTermQuery(
Expression<Func<User, bool>> predicate, string searchTerm)
{
if(String.IsNullOrWhiteSpace(searchTerm)
return predicate;
// You don't need to use ToUpper() unless you
// know that your database performs case-sensitive comparison
return predicate.Or(m => m.field1.Contains(searchTerm))
.Or(m => m.field2.Contains(searchTerm)); // etc.
}
拥有所有这些查询将如下所示:
var predicate = PredicateBuilder.True<User>()
predicate = BuildIdQuery(predicate, id1, a => a.id1);
predicate = BuildIdQuery(predicate, id2, a => a.id2);
foreach(var term in searchTerms)
{
predicate = BuildSearchTermQuery(predicate, term);
}
var results = db.Users
.Where(predicate)
.Skip(...)
.Take(...);
使用 Linq 或存储过程由你决定。对于数据库中的文本搜索,您可以查看Lucene端口
我建议您使用 N 层将复杂的业务逻辑与表示分开。您以后可以轻松更改/优化它。
/// <summary>
/// Assume you have special class to store users
/// </summary>
public class UserBdo
{
public int id1 { get; set; }
public int id2 { get; set; }
......
}
public class PagingParams
{
public int CurrentPage { get; set; }
public int PerPage { get; set; } = 10;
/// <summary>
/// How many without paging
/// </summary>
public int TotalResults { get; set; }
}
public class UsersBll
{
public IEnumerable<UserBdo> Search(int? id1, int? id2, int? id3, IEnumerable<string> searchTerms, PagingParams pp)
{
var results = db.Users.Include(u => u.table1).
Include(u => u.table2).Include(u => u.table3).
Include(u => u.table4).Include(u => u.table5).
Where(u => u.Roles.Any(s => s.RoleId == VendorRole.Id));
if (id1.HasValue)
{
results.Where(m => m.table1.First(a => a.id1 == id1) != null);
}
if (id2.HasValue)
{
results.Where(m => m.table1.First(a => a.id2 == id2) != null);
}
//results = ...
pp.TotalResults = 100;
return results.Skip((pp.CurrentPage - 1) * pp.PerPage).Take(pp.PerPage).ToListAsync();
}
}