31

I have a textbox that allows a user to specify a search string, including wild cards, for example:

Joh*
*Johnson
*mit*
*ack*on

Before using LINQ to Entities, I had a stored procedure which took that string as parameter and did:

SELECT * FROM Table WHERE Name LIKE @searchTerm

And then I would just do a String.Replace('*', '%') before passing it in.

Now with LINQ to Entities I am trying to accomplish the same thing. I know there is StartsWith, EndsWith and Contains support, but it won't support it in the way that I need.

I read about "SqlMethods.Like" and tried this:

var people = from t in entities.People
             where SqlMethods.Like(t.Name, searchTerm)
             select new { t.Name };

However I am getting the following exception:

LINQ to Entities does not recognize the method 'Boolean Like(System.String, 
System.String)' method, and this method cannot be translated into a store 
expression.

How would I get this same functionality using LINQ to Entities?

esac
  • 24,099
  • 38
  • 122
  • 179

11 Answers11

35

http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/6529a35b-6629-44fb-8ea4-3a44d232d6b9/

var people = entities.People.Where("it.Name LIKE @searchTerm", new ObjectParameter("searchTerm", searchTerm));
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • 1
    var people = entities.People.Where("it.Name LIKE '%' + @searchTerm + '%'", new ObjectParameter("searchTerm", searchTerm)); – Evgeni Nabokov Nov 13 '13 at 19:14
  • @EvgeniNabokov That's only necessary when the percentage-signs are not already included in the `searchTerm`. – Pieter Sep 09 '14 at 10:00
  • 16
    Getting an error "no overload for method where takes 2 arguments". Any ideas why? – Dowlers Apr 25 '16 at 22:01
  • If you do not want to use generic queries, you can check out my answer here: http://stackoverflow.com/a/29298128/448355 – Jon Koeter Nov 08 '16 at 13:40
12

How to get it to work seamlessly:

in your EDMX model, add:

    <Function Name="String_Like" ReturnType="Edm.Boolean">
      <Parameter Name="searchingIn" Type="Edm.String" />
      <Parameter Name="lookingFor" Type="Edm.String" />
      <DefiningExpression>
        searchingIn LIKE lookingFor
      </DefiningExpression>
    </Function>

just after the sections that start:

<edmx:ConceptualModels> <Schema Namespace="Your.Namespace"...

Then, anywhere in your code, add this extension method:

    //prior to EF 6 [System.Data.Objects.DataClasses.EdmFunction("Your.Namespace", "String_Like")]

    //With EF 6
    [System.Data.Entity.DbFunction("Your.Namespace", "String_Like")]
    public static bool Like(this string input, string pattern)
    {
        /* Turn "off" all regular expression related syntax in
         * the pattern string. */
        pattern = Regex.Escape(pattern);

        /* Replace the SQL LIKE wildcard metacharacters with the
         * equivalent regular expression metacharacters. */
        pattern = pattern.Replace("%", ".*?").Replace("_", ".");

        /* The previous call to Regex.Escape actually turned off
         * too many metacharacters, i.e. those which are recognized by
         * both the regular expression engine and the SQL LIKE
         * statement ([...] and [^...]). Those metacharacters have
         * to be manually unescaped here. */
        pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^");

        return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
    }

And there you have it.

Now you can do:

(from e in Entities
 where e.Name like '%dfghj%'
 select e)

or

string [] test = {"Sydney", "Melbourne", "adelaide", "ryde"};

test.Where(t=> t.Like("%yd%e%")).Dump();
mlipman
  • 356
  • 3
  • 8
  • 1
    The specified method 'Boolean Like(System.String, System.String)' on the type '...SearcherHelper' cannot be translated into a LINQ to Entities store expression. :( – Gary Jan 10 '13 at 16:11
  • I dont understand how this works because in my case the application dont run through the extension method... any detailed descriptions about functioning?? – Kimbo Feb 20 '13 at 09:41
  • The EDMX lines (above) will make it work with LINQ to Entities. For non-LINQ to entities, the method will get called – mlipman Aug 09 '13 at 12:53
  • LINQ approach is not working, I can't figure out why. Can you help me? Edmx lines are ok, extensions ok, now I don't know what else I could do to have `from ... where ... like`. – natenho Aug 19 '13 at 14:49
  • THANK YOU. This is EXACTLY what I have been looking for! – Ethan Reesor Oct 02 '14 at 16:37
  • 1
    Doesn't support anymore in EF6 because function must be composable. But composable function cannot be defined commandtext. –  Dec 27 '14 at 13:51
9

Well, your choices are:

  • Use Contains. I know you don't like it, but it could probably be made to work.
  • Pick a function from SqlFunctions. They're all supported in L2E.
  • Map your own function.
  • +1 to @Yury for ESQL.
Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • I do not 'dislike' Contains. It doesn't work as intended, see my response below to Mike M. There is nothing in SqlFunctions that will include a LIKE clause. The map your own function is obvious, and is essentially what I was doing anyway, but it seems like this is such a simple ask that it would be supported natively. – esac Jun 22 '10 at 18:31
  • 1
    Between `Contains`, `StartsWith`, and `EndsWith` you can choose which wildcards you need. I believe that covers all your cases. – Craig Stuntz Jun 22 '10 at 19:00
  • Yes, I would use Contains, StartsWith and EndsWith - THOSE SQLMETHODS for LIKE are too flaky – Tom Stickel Oct 20 '15 at 17:26
  • 1
    @CraigStuntz SqlFunctions link is broken – Brian Webster Oct 15 '20 at 21:44
  • @BrianWebster Link updated – Craig Stuntz Oct 17 '20 at 01:06
3

You can do all these statements with LINQ like this

string _search = "johnson";
// joh* OR joh%
items.Where(i => i.Name.StartsWith(_search, StringComparison.OrdinalIgnoreCase));
// *son OR %son
items.Where(i => i.Name.EndsWith(_search, StringComparison.OrdinalIgnoreCase));
// *hns* OR %hns%
items.Where(i => i.Name.ToLower().Contains(_search));
sairfan
  • 970
  • 2
  • 12
  • 20
3

the solution is to use SQLFunctions.PatIndex

var result = from c in items
             where SqlFunctions.PatIndex(searchstring.ToLower(), c.fieldtoSearch) > 0
             select c;

where 'searchstring' is the pattern to search 'fieldtoSearch' is the field to search

Patindex() supports search using string pattern search. The search is case insensitive.

Ram
  • 15,908
  • 4
  • 48
  • 41
3

Now, EF supports "LIKE" usage and you can use all sql wildcards. Check this out.

var people = from t in entities.People
             select new { t.Name };
people = people.Where(x => DbFunctions.Like(x.Name, searchTerm));
boyukbas
  • 1,137
  • 16
  • 24
3

You can do this:

using System.Data.Entity;  // EntityFramework.dll v4.3
var queryResult=db.Accounts.AsQueryable().Where(x => x.Name.Contains(queryKey));

because Linq to Entity can't transform the method Contains() to the SQL, but Linq to SQL can do this. I tried to find a method that can doing a cast, at last, AsQueryable(), also a generic version AsQueryable<T>(). I found I can do this using it this way in my case, but any side effect it has I don't know, maybe it will lose some feature at Entity.

Peter Oram
  • 6,213
  • 2
  • 27
  • 40
Athson
  • 31
  • 1
1
var people = from t in entities.People
                 where t.Name.ToLower().Contains(searchTerm.ToLower())
                 select new { t.Name };

EDIT- I might be mixing syntax. I usually use extension methods; but contains will work.

Mike M.
  • 12,343
  • 1
  • 24
  • 28
  • Except that contains will automatically wrap the whole search term in wild cards. What if the user wants all people whose name starts with "Mar" Mar* Returned Mary Marissa Omar Jamaraquoui (Names have been changed to protect the innocent) – esac Jun 22 '10 at 18:24
  • What is even odder is that it is adding in a '~' as well. For a blank search term, searchTerm is "%". When it queries the database, this is what Contains turns it into: @p__linq__2=N'%~%%' – esac Jun 22 '10 at 18:27
  • The ~ is probably being used as an escape character to show that it's looking for strings with the literal "%" within it. If you're going to use Contains (which handles the wild carding) then you can't also try to handle the wild carding. – Tom H Jun 22 '10 at 19:36
  • Since I haven't really used LINQ to SQL... is it smart enough to know whether or not the underlying DB is case sensitive when coming up with its SQL? This more applies to StartsWith, but the .ToLower could kill the use of indexes. – Tom H Jun 22 '10 at 20:39
  • @Tom - You know, I'm not even sure. That's a very good question and I might need to do some playing tonight... – Mike M. Jun 22 '10 at 20:53
  • I hate the ESCAPE '~' that it adds whenever you use field.Contains(searchTerm) – Gary Jan 10 '13 at 16:11
  • When using .Contains With SQL Server, it brings the case-insensitive results if DB collation is *case insensitive* (unlike .ToList().Where(..Contains() which pulls all data to memory and is performed by .net in case-sensitive way) – Ekus Oct 20 '17 at 17:50
1

It is easily achieved by following methods

var people = from t in entities.People
             where t.Name.Contains(searchTerm)
             select new { t.Name };

Use the following specifications to achieve wildcards

LIKE 'a%' => StartsWith("a")
LIKE '%a' => EndsWith("a")
LIKE '%a%' => Contains("a")
LIKE 'a%b' => StartsWith("a") && EndsWith("b")
LIKE '%a%b%' => StartsWith("a") && Contains("b")
Shyam Bhagat
  • 759
  • 6
  • 7
0

You do not need to use percent sign while filtering. e.g;

if I want to check ItemName does not contain '-' I will do it like this

!Item.ItemName.Contains("-")

In SQL it will convert to NOT LIKE '%-%'

Ali Sakhi
  • 195
  • 1
  • 1
  • 9
0

We use Database First and the EntityFramework.

The "Map your own function." approach works for us together with the nuget EntityFramework.CodeFirstStoreFunctions.

1 Step: Create a function in the db like this:

CREATE FUNCTION [dbo].[StringLike]
(
      @a nvarchar(4000),
      @b nvarchar(4000)
)
RETURNS bit
AS
BEGIN
    RETURN 
    (SELECT CASE
            WHEN (SELECT 1 WHERE @a LIKE @b) = 1 THEN 1
            ELSE 0
            END)  
END

2 Step: Install nuget EntityFramework.CodeFirstStoreFunctions

3 Step: Create a method in your code like this (I create mine in the DbContext class):

[DbFunction("CodeFirstDatabaseSchema", "StringLike")]
public static bool Like(string input, string pattern)
{
    throw new NotSupportedException("Direct calls are not supported.");
}

4 Step: Initalize EntityFramework.CodeFirstStoreFunctions.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new FunctionsConvention("dbo", this.GetType()));
}

5 Step: Now you can use this method in your linq query.

Markus
  • 3,871
  • 3
  • 23
  • 26