簡體   English   中英

在Linq-to-Entities查詢表達式中使用元組或其他復雜類型

[英]Using a tuple or some other complex type in a Linq-to-Entities query expression

因此,我想在客戶表中搜索所有具有相同名稱,電子郵件地址或電話號碼的所有查詢關鍵字都匹配的客戶。

...用英語比用英語更容易理解:

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的原始值又存儲Pack ed值的格式,而我必須在客戶端執行此操作。 問題在於Linq不喜歡在表達式參數中包含元組或數組,並且不支持String.IndexOf ,因此我不能將兩個字符串合而為一,然后接受子字符串。

還有其他解決方法嗎? 還是重述查詢?

編輯:生成的SQL看起來像這樣:

SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName], 
(etc)
FROM [dbo].[Contacts] AS [Extent1]
WHERE ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
    WHERE ( NOT ([Extent1].[FirstName] = N'rei' OR [Extent1].[LastName] = N'rei' OR [Extent1].[EmailAddress] = N'rei' OR [Extent1].[HomePhone] = N'rei' OR [Extent1].[CellPhone] = N'rei')) OR (CASE WHEN ([Extent1].[FirstName] = N'rei' OR [Extent1].[LastName] = N'rei' OR [Extent1].[EmailAddress] = N'rei' OR [Extent1].[HomePhone] = N'rei' OR [Extent1].[CellPhone] = N'rei') THEN cast(1 as bit) WHEN ( NOT ([Extent1].[FirstName] = N'rei' OR [Extent1].[LastName] = N'rei' OR [Extent1].[EmailAddress] = N'rei' OR [Extent1].[HomePhone] = N'rei' OR [Extent1].[CellPhone] = N'rei')) THEN cast(0 as bit) END IS NULL)
))
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變量將解決您的問題。

我將創建一個私有結構:

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))));
}

還要注意,我發現了另一種獲取跟蹤輸出的方法

IQueryable<Person> query = SearchCustomers(entities, "w1 w2",
                                           "(602) (408)");
var oc = (ObjectQuery<Person>) query;
Console.WriteLine(oc.ToTraceString());

請注意, 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

我將其分為兩種方法:

  • 搜索客戶
  • 搜索客戶電話號碼

在SearchCustomerPhoneNumber中,您在執行查詢之前將參數轉換為壓縮的。

由於電話號碼將不包含字母,而其他號碼將包含字母,因此可以檢查應運行哪種方法。 拆分實際上將減少數據庫的負載。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM