如何解决C#linq查询-如何使用值列表在内部表上进行查询
我正在使用C#EntityFramework linq查询。我在编写以下方案时遇到麻烦。 我有个人表和别名表,并且表之间有外键关系。如果用户以“姓氏姓氏”或“姓氏姓氏”搜索,则需要给出结果(如果它存在于persontable中),否则需要查看别名表以获取结果。在database中,全名遵循格式“ lastname firstname”或“ firstname lastname”。
Person Table: (Id,Fullname) Alias Table:(Id PersonId Fullname)
-
我正在按用户使用空格拆分名称搜索,并将这些字符串存储在数组中。
-
我能够从“人”表中成功获取结果。我无法从别名表获取结果。
-
以下是我正在处理的查询:
。
string[] lsNames = val.Split(' ',','&');
var a = this.context.Person
.Include(x => x.Alias)
.Where(x => x.Alias.Any(v =>
lsNames.All(n=>v.Name.ToLower().Contains(n.ToLower()))));
我需要返回人员表并包括别名。如何使用linq查询在别名表中搜索值列表。
以上查询使我的错误评估超时。谁能帮我解决这个问题。
解决方法
如果您的应用程序要求存储的全名是“ {first} {last}”和-或“ {last} {first}”,则问题是您想比较用户输入,这可能是任何合理的用户格式,那么我将考虑解析输入字符串,确定它是否特别有效,然后根据该字符串发出特定条件。
例如,如果数据存储为“ {first} {last}”:
if (string.IsNullOrEmpty(name))
return null; // or handle no name search provided.
var nameParts = name.Split(new [] {' ',','&'},StringSplitOptions.RemoveEmptyEntries);
var query = Context.Person.AsQueryable();
if (nameParts.Length == 1)
query = query.Where(x => x.Fullname.Contains(nameParts[0]));
else if (nameParts.Length == 2)
{
var combination1 = string.Join(" ",nameParts);
Array.Reverse(nameParts);
var combination2 = string.Join(" ",nameParts);
query = query.Where(x => x.Fullname == combination1 || x.Fullname == combination2 );
}
else
{ // More than 2 name components,so assume they are typing
// "{first} {middle} {last}" or "{last},{first} {middle}"
var combination1 = string.Join(" ",nameParts);
// Shift the first element (last name) to the end
var firstElement = nameParts[0];
Array.Copy(nameParts,1,nameParts,nameParts.Length- 1);
nameParts[nameParts.Length - 1] = firstElement;
var combination2 = string.Join(" ",nameParts);
query = query.Where(x => x.Fullname == combination1 || x.Fullname == combination2);
}
var people = query.ToList();
这对如何搜索输入字符串施加了限制。假定根据SQL Server / w大多数默认关联,通过EF进行的数据库比较将不区分大小写。如果数据库比较是区分大小写的,那么希望数据总是以一致的大小写存储(总是大写或总是小写),那么可以在上面应用适当的大小写。理想情况下,仅输入表达式中的字符串,而不是评估字符串和实体/ db列。 (数据库需要做更多的工作)
以上情况限制了以下检查:如果用户键入单个单词(例如“ Peter”),它将找到“ Peter Smith”或“ Mitch Peterson”。如果他们键入“ Peter Smith”或“ Smith,Peter”,例如会匹配“彼得·史密斯”而不是“彼得·史密斯”。在组合之间进行较宽松的比较将导致SQL最终需要处理的工作量更多。引入用于输入的规则/期望并对其进行验证将产生更多的代码,但查询更简单/更快。类似于Contains
的查询的风险在于,这使用户有可能使用诸如“我是邪恶的b或c或d或e”之类的字符串来限制查询,想像一下{{1 }}与Contains
/'All`相结合,将在该结果数组中产生组合。通常,与用户输入相比更为悲观,并且让用户优化其输入内容比尝试适应接近匹配更为安全,尤其是在不处理诸如.FirstName和.LastName之类的特定列的情况下。
您必须使用可以建立以下谓词的帮助程序:
Expression<Func<Alias,boo>> aliasPredicate = a =>
a.Name.ToLower().Contains(lsNames[0]) ||
a.Name.ToLower().Contains(lsNames[1]) ...
);
例如使用LINQKit
string[] lsNames = val.ToLower().Split(' ','&');
var builder = PredicateBuilder.New<Alias>();
foreach (var n in lsNames)
{
var name = n; // avoid closure capturing of n variable
builder = builder.Or(a => a.Name.ToLower().Contains(name));
}
var personQuery = this.context.Person
.Where(x => x.Alias.AsQueryable().Any(builder));
注意,它将仅检索人员,而不检索其别名。
Include(x => x.Alias)
可能会降低查询速度。为了获得更好的性能,我们必须使用客户端后期处理来创建更复杂的解决方案。
使用标准IQueryable
扩展名和EF Core时,建议的解决方案几乎是最好的。注意,还有一些数据库特定功能,例如全文搜索,可以显着改善查询。
如果遇到超时错误,则需要检查索引以确保Name被索引并且索引被正确使用。例如,如果将列设置为varchar而不是nvarchar,则SQL将在where子句中进行类型强制转换,结果可能会忽略索引。您可以在必要时强制EF不要使用unicode,但是语法会根据所使用的EF版本和配置映射的方式而有所不同。
此外,就像@Steve Py提到的那样,您不需要对字符串执行ToLower,因为大多数SQL查询都不区分大小写。
,如果将名称拆分为多个部分并想在这些部分上搜索匹配项,那么您正在加入集合,而当我加入集合时却无法使用导航属性,我通常会选择查询语法:>
var query = from person in this.context.Person
from name in lsNames
where person.Name.StartsWith(name) || person.Aliases.Any(a => a.Name.StartsWith(name))
select person;
return query.ToList()
这可能不是最有效的搜索,因为它使用通配符,并且您可能需要完全匹配。
如果这样做,则可以使用StevePy的技术填充lsNames数组并使用类似的查询:
string[] lsNames = {combination1,combination2)
var query = from person in this.context.Person
from name in lsNames
where person.Name == name || person.Aliases.Any(a => a.Name == name)
select person;
return query.ToList()
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。