Using a tuple or other complex type in a Linq-to-Entities query expression

So, I want to look in the customer table for all customers, each of which has its own name, email address or phone numbers that correspond to all the keywords of the request.

... Which is probably easier to understand in code than in English:

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

But I can’t call PhoneNumber.Packin the database, so I need to make a wformat that saves both the original value wand the Packed value , and I do this on the client side. The problem is that Linq does not like to have tuples or arrays in the arguments of an expression, and it does not support String.IndexOf, so I cannot execute two lines in one and then take substrings.

Any other ways around this? Or maybe a repeat request?

: 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)
))
+5
4
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;
}

, temp foreach .

+3

:

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 SQL ws. , .

, , . , , , . 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());
+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
+1
source

I would divide it into 2 methods:

  • SearchCustomer
  • SearchCustomerPhoneNumber

In SearchCustomerPhoneNumber, you convert the parameter to a batch package before executing the query.

Since the phone number will not contain letters, and the rest will be available, you can check which of the methods should be launched. Partitioning will actually reduce the load on the database.

0
source

All Articles