Question

I have the following LINQ query:

var aKeyword = "ACT";
var results = from a in db.Activities
              where a.Keywords.Split(',').Contains(aKeyword) == true
              select a;

Keywords is a comma delimited field.

Everytime I run this query I get the following error:

"LINQ to Entities does not recognize the method 'Boolean Contains[String](System.Collections.Generic.IEnumerable`1[System.String], System.String)' method, and this method cannot be translated into a store expression."

What is the alternative for what I am trying to do?

Was it helpful?

Solution

In response to your performance considerations on a big dataset:

You are going to be doing non indexed wildcard string matching on the client, so yes, there will be performance loss.

Is there a reason why you have multiple keywords in one table field? You could normalize that out, to have a ActivityKeywords table where for each Activity you have a number of Keyword records.

Activities(activity_id, ... /* remove keywords field */) ---> ActivityKeywords(activity_id, keyword_id) ---> Keywords(keyword_id, value)

Check out Non-first normal form: http://en.wikipedia.org/wiki/Database_normalization

EDIT: Also even if you were to stick with the one column, there is a way to do everything serverside (if you have a strict syntax: 'keyword1, keyword2, ..., keywordN'):

var aKeyword = "ACT";
var results = (from a in db.Activities
              where a.Keywords.Contains("," + aKeyword) || a.Keywords.Contains(aKeyword + ",")
              select a;

OTHER TIPS

Your problem is that LINQ-to-Entites has to translate everything you give it into SQL to send to the database.

If that is really what you need to do, you'll have to force LINQ-to-Entities to pull back all the data and LINQ-to-Objects to evaluate the condition.

Ex:

var aKeyword = "ACT";
var results = from a in db.Activities.ToList()
              where a.Keywords.Split(',').Contains(aKeyword) == true
              select a;

Be aware though, that this will pull back all the objects from the Activities table. An alternative may be to let the DB do a bit of an initial filter, and filter down the rest of the way afterwards:

var aKeyword = "ACT";
var results = (from a in db.Activities
              where a.Keywords.Contains(aKeyword)
              select a).ToList().Where(a => a.KeyWords.Split(',').Contains(aKeyword));

That will let LINQ-to-Entities do the filter it understands (string.Contains becomes a like query) that will filter down some of the data, then apply the real filter you want via LINQ-to-Objects once you have the objects back. The ToList() call forces LINQ-to-Entities to run the query and build the objects, allowing LINQ-to-Objects to be the engine that does the second part of the query.

My guess is the way you are calling Split. It should take an array. Maybe there is another Split in Linq it is finding and giving you an unusual error:

This works for Linq to Objects:

 var dataStore = new List<string>
                    {
                        "foo,bar,zoo",
                        "yelp,foo",
                        "fred",
                        ""
                    };
 var results = from a in dataStore
               where a.Split(new[] {','}).Contains("foo")
               select a;

 foreach (var result in results)
 {
     Console.WriteLine("Match: {0}", result);
 }

Outputs the following:

Match: foo,bar,zoo
Match: yelp,foo

Actually, thinking about it, do you need the split at all? a.Contains("foo") may be enough for you (unless you don't want to hit foobar).

You may want to look at this question about L2E and .Contains for a solution that should be more efficient than guessing at a superset before filtering client side.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top