LINQ to SQL to find match in results that have multiple pipe separated values - linq

I have a table that has multiple columns, one of which contains pipe separated values.
I found an answer that's partially what I'm looking for here but that assumes you're searching in one CSV-type list.
What I have is rows of data, one column (called serviceIDs) has data like 2|45|5|6
I want to be able to pass a value into a query something like this:
Select all rows where serviceIDs contains '5'
or
Select all rows where serviceIDs like '%5%'
But obviously neither of those are going to work properly. Is there a way to do this in LINQ?

Unfortunately Split isn't supported in LINQ to SQL. You could do something like the following where you tack on a leading and trailing pipe and then use contains with the leading and trailing pipe. That would solve the issue where finding 45 when searching for 5. Be aware that this could kill your performance because the calculated column value would block index usage in the database. If you store the original values with the leading and trailing pipe, the indexing would be better (but not as good as using a full text search).
var yourvalue = "5";
var expectedResult = "|" + yourvalue + "|";
var resultset = rows.Where(row => ("|" + row.serviceIDs + "|").Contains(expectedResult);

var yourvalue = "5";
var resultset = rows.Where(row => row.serviceIDs.Split('|').Contains(yourvalue));

disclaimer: i never used linq for DB stuff, but something along these lines might work
yourTable.AsEnumerable()
.Select(row => row["serviceIDs"].ToString().Split('|'))
.ToList()
.Where(s => s == 5);

Related

check one CSV is contained in another CSV column in database LINQ to Entities

I have a table called Products having column called ApprovedState which contains values like "AL,AK,AR". I'm getting a CSV list from front-end as listCSVState='AL,AK'.
Can someone help me to write a LINQ query which will return me all products which are approved in listCSVState. I have tried following code but not getting the correct result.
from product in db.Products where ((listCSVState== null) || (listCSVState.Contains(product.StateApprovals)))
Trivial in LINQ to Objects, tricky in LINQ to Entities due to lack of string.Split support.
Still, a combination of Any, string.Contains and string concatenations can do the job, like this
var states = listCSVState.Split(',');
var query = db.Products.Where(product => states.Any(state =>
("," + product.StateApprovals + ",").Contains("," + state + ",")));

Finding items from a list in an array stored in a DB field

I have a legacy database that has data elements stored as a comma delimited list in a single database field. (I didn't design that, I'm just stuck with it.)
I have a list of strings that I would like to match to any of the individual values in the "array" in the DB field and am not sure how to do this in Linq.
My list:
List<string> items= new List<string>();
items.Add("Item1");
items.Add("Item2");
The DB field "Products" would contain data something like:
"Item1,Item3,Item4"
"Item3,Item5,Item6"
"Item2,Item7,Item6"
"Item1,Item2"
"Item1"
My first pass at the Linq query was:
var results = (from o in Order
.Where(p=> items.Contains(p.Products)
But I know that won't work. because it will only return the records that contain only "Item1" or "Item2". So with the example data above it would return 0 records. I need to have it return two records.
Any suggestions?
There is a simple clever trick for searching comma-separated lists. First, add an extra , to the beginning and end of the target value (the product list), and the search value. Then search for that exact string. So for example, you would search ,Item1,Item3,Item4, for ,Item1,. The purpose of this is to prevent false positives, i.e., Item12,Item3 finding a match for Item1, while allowing items at the beginning/end of the list to be properly found.
Then, you can use the LINQ .Any method to check that any item in your list is a match to the product list, like the following:
var results = (from o in Order
.Where(o => items.Any(i => (","+o.Products+",").Contains(","+i+",")))
One way would be to parse the list in the Products field:
var results = (from o in Order
.Where(o => items.Any(i => o.Products.Split(',').Contains(i))
But that would parse the string multiple times for each record. You could try pulling back ALL of the records, parsing each record once, then doing the comparison:
var results = from o in Order
let prods = o.Products.Split(',')
where items.Any(i => prods.Contains(i))
select o;

How do I filter in linq query when field needs parsing first?

I have a data table containing multiple columns and one column that stores somewhat complex text patterns - I need to parse the field to determine if a particular sub strings exist in specific positions within the larger string pattern and then if the record should be filtered out as a result.
I can't see a way to perform the parse other than by writing a C# parsing function with String.Split method calls, foreach, etc. But if I try to parse like this:
var myFilteredTable = _db.MyTable.Where(t => t.Column1 == 'Filter1'
&& ParseIsMyItemInColumn2(t) );
I get "has no supported translation to SQL" errors.
The other option I thought of was to build the initial result without the Parse:
var myFilteredTable = _db.MyTable.Where(t => t.Column1 == 'Filter1' );
and iterate through the IQueryable resultset, testing each row with the parse function, to filter out the unwanted rows, but IQueryable does not have Remove function to strip out unwanted rows nor Add function to allow me to build up a new resultset.
So how can I filter in linq when I also need to write a Parse function?
Well the "initial filter in the database then do the rest locally" is easy:
var filtered = _db.MyTable.Where(t => t.Column1 == "Filter1")
.AsEnumerable() // Do the rest locally
.Where(t => ParseIsMyItemInColumn2(t));
AsEnumerable is a simple pass through method, but because the result is typed as IEnumerable<T> rather than IQueryable<T>, the subsequent LINQ operations use the LINQ to Objects methods in Enumerable rather than the ones in Queryable.
Obviously if a lot of items match the first filter but fail the second, that won't be terribly efficient...
Unfortunately, if the "parse function" is not something that can be translated to SQL, you will need to pull the results and use LINQ to Objects:
var myFilteredTable = _db.MyTable.Where(t => t.Column1 == 'Filter1')
.AsEnumerable().Where(ParseIsMyItemInColumn2);
Note that this will stream all of the results into memory, and then perform your parse.

Finding Last Name Using LINQ

I am trying to get last name using linq in visual studio. In my database, i have the Field name like "FullName".
In this Field I have value like "Subbu Cargos"
I Wanna Display the "Cargos" in my textbox.
How can i make a simple linq query?
Would it be over simple to say:
return FullName.Split(' ').Last()
?
I would suggest breaking it up into different fields - Firstname, Middlename, lastname, Title - and rebuilding the name on the fly when you come to display it.
If you're still determined to use one field, then consider a query like:
string s = "Subba Cargos";
var lastnames = from name in s.Split(new Char[] { ' ' }).Last<string>()
select name;
I would suggest not trying to parse out the last name. Like you say, first and last names could be switched around, someone might have a second name, or a last name that consists of multiple words (“van Dijk”), or may not have entered a last name at all.
Check out this article: Falsehoods Programmers Believe About Names
If you still want to do this however, try something like this:
customers.Select(c => c.FullName.Split(' ').Last());
You might not be able to this on the server side. In that case:
customers
.Select(c => c.FullName)
.ToList()
.Select(n => n.Split(' ').Last());
Untested, but this should give a rough idea.
You could also do it like this:
customers
.Select (b => b.FullName.Substring ((b.FullName.IndexOf(' ') + 1)));

NHibernate advanced search headache

I really believe this is a simple problem, but one of those with a solution that's not obvious to an NHibernate newbie like me...
Here's the deal, I'm conducting my NHibernate-related queries from a data service layer that knows nothing about NHibernate (for separation of concerns). As such, I'm constructing my queries by using LINQ (Sytem.Linq).
I want to search on more than one word. For instance, if someone types in "training excel", then I will search a number of entities and related location entities based on both words.
Here's what my code looks like in my service layer right now:
// We are delimiting by spaces and commas.
string delimiterString = " ,";
char[] delimiter = delimiterString.ToCharArray();
IEnumerable<string> words = searchWords.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
// Loop through each search word in the collection and apply the "Where" clause to our IQueryable collection:
foreach (string word in words) {
matches = matches.Where(i => i.Subject.Contains(word)
|| i.Background.Contains(word)
|| i.Summary.Contains(word)
|| i.Organization.Contains(word)
|| i.Locations.Any(l => l.Organization.Contains(word))
|| i.Locations.Any(l => l.City.Contains(word))
);
}
Here's the issue... through viewing my application logs and running NHibernate Profiler, I see that the T-SQL query is correctly being constructed. The search works fine with just one search word passed in. However, if 2 or more words are detected, the last word detected is the only one searched on. For instance, if the search term was "training excel", then, as I step through my code above, both words are correctly added in the loop, but the final T-SQL output has "excel" in both logical where groups in the WHERE clause (i.e. WHERE course.Subject like ('%' + 'excel' + '%')...... AND course.Subject like ('%' + 'excel' + '%')......). There should have been "training" in the first group and "excel" in the second group.
It seems like NHibernate is using some sort of query caching for efficiency because the query signature is the same (since we're looping through all the words). Again, I have verified that both words are being used when stepping through my code.
Any ideas??
This must be the common pitfall "access to modified closure". Try
foreach (string word in words)
{
var wordLoopVariable = word;
matches = matches.Where(i => i.Subject.Contains(wordLoopVariable)
|| i.Background.Contains(wordLoopVariable)
|| i.Summary.Contains(wordLoopVariable)
|| i.Organization.Contains(wordLoopVariable)
|| i.Locations.Any(l => l.Organization.Contains(wordLoopVariable))
|| i.Locations.Any(l => l.City.Contains(wordLoopVariable))
);
And do some googling on closures.

Resources