所以我想在一个客户表中搜索所有客户,每个客户的名称,电子邮件地址或电话号码都与所有查询关键字匹配。
...在代码中比在英语中更容易理解:
public IQueryable<Contact> SearchCustomers(string query)
{
var ws = from w in query.Split()
where !String.IsNullOrWhiteSpace(w)
select w;
var q =
from c in Customers
where ws.All(w =>
c.FirstName == w
|| c.LastName == w
|| c.EmailAddress == w
|| c.HomePhone == PhoneNumber.Pack(w)
|| c.CellPhone == PhoneNumber.Pack(w))
select c;
return q;
}
但我不能在数据库上调用PhoneNumber.Pack
,所以我需要使w
格式存储w
的原始值以及{{1} ed值,我必须在客户端做到这一点。问题是Linq不喜欢在表达式参数中使用元组或数组,并且它不支持Pack
,所以我不能在一个中抛出两个字符串然后使用子字符串。
还有其他方法来解决这个问题吗?或者可能重述一下查询?
编辑:生成的SQL如下所示:
String.IndexOf
答案 0 :(得分:3)
public IQueryable<Contact> SearchCustomers(string query)
{
var ws = from w in query.Split()
where !String.IsNullOrWhiteSpace(w)
select new { Unpacked = w , Packed = PhoneNumber.Pack(w) };
var q = Customers;
foreach(var x in ws)
{
string ux = x.Unpacked;
string px = x.Packed;
q = q.Where(
c=>
c.FirstName == ux
|| c.LastName == ux
|| c.EmailAddress == ux
|| c.HomePhone == px
|| c.CellPhone == px
);
}
return q;
}
这将产生所需的结果,foreach中的temp变量将解决您的问题。
答案 1 :(得分:1)
我要创建一个私有结构:
private struct UnpackedAndPacked
{
public string Unpacked {get;set;}
public string Packed {get;set;}
}
var ws = from w in query.Split()
where !String.IsNullOrWhiteSpace(w)
select new UnpackedAndPacked
{
Unpacked=w,
Packed=PhoneNumber.Pack(w)
};
然后改变条件:
where ws.All(w =>
c.FirstName == w.Unpacked
|| c.LastName == w.Unpacked
|| c.EmailAddress == w.Unpacked
|| c.HomePhone == w.Packed
|| c.CellPhone == w.Packed)
select c;
我进一步研究了这一点,我认为你不会按原样完成这项工作。问题在于,由于ws.All
,它希望为ws
序列中的每个值创建一组SQL子句。它需要是一系列原始类型,如字符串。
如果您可以将代码更改为具有两个查询参数,那么我认为它可能有效。对于那些不需要打包的东西,你需要一组参数,而对于那些需要打包的东西,你需要一组参数。然后,您可以将其更改为LINQ方法链,并在两者之间执行联合。示例要遵循。
有效。我的代码如下。请注意,我使用的是AdventureWorks2008R2数据库,因此我的数据比您的更复杂 - 我有一组电子邮件地址和电话要处理;接受其中任何一场比赛:
public static IQueryable<Person> SearchCustomers(
AdventureWorksEntities entities, string nameQuery, string phoneQuery)
{
var wsu = from w in nameQuery.Split()
where !String.IsNullOrWhiteSpace(w)
select w;
var wsp = from w in phoneQuery.Split()
where !String.IsNullOrWhiteSpace(w)
select Pack(w);
return
entities.People.Where(
c => wsu.All(w => c.FirstName == w || c.LastName == w)).
Union(
entities.People.Where(
c =>
wsp.All(
w =>
c.PersonPhones.Any(p => p.PhoneNumber == w) ||
c.EmailAddresses.Any(a => a.EmailAddress1 == w))));
}
另请注意,我找到了another way to get trace output:
IQueryable<Person> query = SearchCustomers(entities, "w1 w2",
"(602) (408)");
var oc = (ObjectQuery<Person>) query;
Console.WriteLine(oc.ToTraceString());
答案 2 :(得分:1)
请注意,query.Where(a).Where(b)
与query.Where(a & b)
相同,而qry.All()基本上采用了一系列条件并将AND
语句链接在一起,例如(word 1 is found) && (word 2 is found) && (word 3 is found)
...
您可以使用它来执行以下操作(我正在使用扩展方法,以便我可以将其链接到任何其他IQueryable<Customer>
的末尾。)
[System.Runtime.CompilerServices.Extension()]
public static IQueryable<Customer> Search(this IQueryable<Customer> query, string searchTerm)
{
string[] queryWords = searchTerm.Split(" ");
foreach (string w in queryWords) {
string word = w;
string packedWord = Pack(word);
query = query.Where(c => c.FirstName == word || c.LastName == word || c.HomePhone == packedWord || c.CellPhone == packedWord);
}
return query;
}
或VB等效
<System.Runtime.CompilerServices.Extension()>
Public Function Search(query As IQueryable(Of Customer), searchTerm As String) As IQueryable(Of Customer)
Dim queryWords = searchTerm.Split(" ")
For Each w In queryWords
Dim word = w
Dim packedWord = Pack(word)
query = query.Where(Function(c) c.FirstName = word OrElse
c.LastName = word OrElse
c.HomePhone = packedWord OrElse
c.CellPhone = packedWord)
Next
Return query
End Function
答案 3 :(得分:0)
我会把它分成两种方法:
在SearchCustomerPhoneNumber中,在执行查询之前将参数转换为packed。
由于电话号码不包含字母而其他电话号码不包含,因此可以检查应该运行哪种方法。拆分实际上会减少数据库的负载。