Mitch
Mitch

Reputation: 2551

LINQ return records where string[] values match Comma Delimited String Field

I am trying to select some records using LINQ for Entities (EF4 Code First).

I have a table called Monitoring with a field called AnimalType which has values such as

I want to pass in some values in a string array (animalValues) and have the rows returned from the Monitorings table where one or more values in the field AnimalType match the one or more values from the animalValues. The following code ALMOST works as I wanted but I've discovered a major flaw with the approach I've taken.

public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
    {
        var result = from m in db.Monitorings
                     where animalValues.Any(c => m.AnimalType.Contains(c))
                     select m;
        return result;
    }

To explain the problem, if I pass in animalValues = { "Lion", "Tiger" } I find that three rows are selected due to the fact that the 4th record "Mountain Lion" contains the word "Lion" which it regards as a match.

This isn't what I wanted to happen. I need "Lion" to only match "Lion" and not "Mountain Lion".

Another example is if I pass in "Snake" I get rows which include "Rattlesnake". I'm hoping somebody has a better bit of LINQ code that will allow for matches that match the exact comma delimited value and not just a part of it as in "Snake" matching "Rattlesnake".

Upvotes: 2

Views: 4595

Answers (2)

Ivo
Ivo

Reputation: 8362

This is a kind of hack that will do the work:

public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
    var values = animalValues.Select(x => "," + x + ",");
    var result = from m in db.Monitorings
                 where values.Any(c => ("," + m.AnimalType + ",").Contains(c))
                 select m;
    return result;
}

This way, you will have

  • ",Lion,Tiger,Goat,"
  • ",Snake,Lion,Horse,"
  • ",Rattlesnake,"
  • ",Mountain Lion,"

And check for ",Lion," and "Mountain Lion" won't match.

It's dirty, I know.

Upvotes: 4

Brad Rem
Brad Rem

Reputation: 6026

Because the data in your field is comma delimited you really need to break those entries up individually. Since SQL doesn't really support a way to split strings, the option that I've come up with is to execute two queries.

The first query uses the code you started with to at least get you in the ballpark and minimize the amount of data you're retrieving. It converts it to a List<> to actually execute the query and bring the results into memory which will allow access to more extension methods like Split().

The second query uses the subset of data in memory and joins it with your database table to then pull out the exact matches:

public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
    // execute a query that is greedy in its matches, but at least
    // it's still only a subset of data. The ToList()
    // brings the data into memory, so to speak
    var subsetData = (from m in db.Monitorings
             where animalValues.Any(c => m.AnimalType.Contains(c))
             select m).ToList();

    // given that subset of data in the List<>, join it against the DB again
    // and get the exact matches this time
    var result = from data in subsetData
            join m in db.Monitorings on data.ID equals m.ID
            where data.AnimalType.Split(',').Intersect(animalValues).Any ()
            select m;        

    return result;
}

Upvotes: 2

Related Questions