复杂的 mvc linq 生成 sql 脚本
complicated mvc linq generated sql script
我是 MVC 世界的新手,但我已经 asp.net 多年了。我用 mvc 开始了这个项目,所以我可以习惯它。我一直在努力处理 linq 生成的一些过于复杂的 sql 语句,我想我做错了什么。如果不从头开始编写 sql 语句,我就无法分辨。
好的,我的目标是获得这个身份数据库,我添加了一些字段并链接到其他一些表,如地址和电话号码等。
我正在处理供应商搜索屏幕,想使用 linq 来获取数据。在下面的示例中,一些信息位于 table1 的下拉列表中,并且有一个文本搜索选项,允许 "OR" 在主 aspnetusers 和其他一些表上的多个字段上搜索。
我希望有人能帮我在 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());
感谢您花时间提供帮助。
用什么Linq还是存储过程就看你的了。
对于数据库中的文本搜索,您可以查看 Lucene port
我建议您使用 N 层将复杂的业务逻辑与表示分离。以后你change/optimize就很容易了。
/// <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();
}
}
您的查询现在看起来,每个 Where
子句都使用集合中的 AND
进行匹配,而不是 OR
。
如果你真的想对所有条件使用OR
,下面的应该有用。
正如我在 , the proper way to build complex query predicates with Linq
is the use of a 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(...);
我是 MVC 世界的新手,但我已经 asp.net 多年了。我用 mvc 开始了这个项目,所以我可以习惯它。我一直在努力处理 linq 生成的一些过于复杂的 sql 语句,我想我做错了什么。如果不从头开始编写 sql 语句,我就无法分辨。
好的,我的目标是获得这个身份数据库,我添加了一些字段并链接到其他一些表,如地址和电话号码等。
我正在处理供应商搜索屏幕,想使用 linq 来获取数据。在下面的示例中,一些信息位于 table1 的下拉列表中,并且有一个文本搜索选项,允许 "OR" 在主 aspnetusers 和其他一些表上的多个字段上搜索。
我希望有人能帮我在 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());
感谢您花时间提供帮助。
用什么Linq还是存储过程就看你的了。 对于数据库中的文本搜索,您可以查看 Lucene port
我建议您使用 N 层将复杂的业务逻辑与表示分离。以后你change/optimize就很容易了。
/// <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();
}
}
您的查询现在看起来,每个 Where
子句都使用集合中的 AND
进行匹配,而不是 OR
。
如果你真的想对所有条件使用OR
,下面的应该有用。
正如我在 Linq
is the use of a 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(...);