Multiple Defered WHERE clause expressions in LINQ to SQL - linq

Maybe a simple question, I'm trying to get a result from a table where the Name column contains all of an array of search terms. I'm creating a query and looping through my search strings, each time assigning the query = query.Where(...);. It appears that only the last term is being used, I supposed because I am attempting to restrict the same field each time. If I call .ToArray().AsQueryable() with each iteration I can get the cumlative restrinction behavior I'm looking for, but it there an easy way to do this using defered operators only?
Thanks!

If you're doing something like:
foreach (int foo in myFooArray)
{
query = query.where(x => x.foo == foo);
}
...then it will only use the last one since each where criteria will contain a reference to the 'foo' loop variable.
If this is what you're doing, change it to:
foreach (int foo in myFooArray)
{
int localFoo = foo;
query = query.where(x => x.foo == localFoo);
}
...and everything should be fine again.
If this is not what is happening, please provide a code sample of what you're doing...

Related

Return Linq query results into List object

I am trying to return the results of a query into a List object, however the following code, as I normally use, does not work. Still relatively new to Linq, can someone explain the correct syntax/what's going on? This will work if I change the data type of productTraining to var...
List<AgentProductTraining> productTraining = new List<AgentProductTraining>();
productTraining = from records in db.CourseToProduct
where records.CourseCode == course.CourseCode
select records;
Select() and Where() will return IQueryable<T>, not List<T>. You've got to convert it to a List<T> - which actually executes the query (instead of just preparing it).
You just need to call ToList() at the end of the query. For example:
// There's no need to declare the variable separately...
List<AgentProductTraining> productTraining = (from records in db.CourseToProduct
where records.CourseCode == course.CourseCode
select records).ToList();
Personally I wouldn't use a query expression though, when all you're doing is a single Where clause:
// Changed to var just for convenience - the type is still List<AgentProductTraining>
var productTraining = db.CourseToProduct
.Where(records => records.CourseCode == course.CourseCode)
.ToList();

Longish LINQ query breakes SQLite-parser - simplify?

I'm programming a search for a SQLite-database using C# and LINQ.
The idea of the search is, that you can provide one or more keywords, any of which must be contained in any of several column-entries for that row to be added to the results.
The implementation consists of several linq-queries which are all put together by union. More keywords and columns that have to be considered result in a more complicated query that way. This can lead to SQL-code, which is to long for the SQLite-parser.
Here is some sample code to illustrate:
IQueryable<Reference> query = null;
if (searchAuthor)
foreach (string w in words)
{
string word = w;
var result = from r in _dbConnection.GetTable<Reference>()
where r.ReferenceAuthor.Any(a => a.Person.LastName.Contains(word) || a.Person.FirstName.Contains(word))
orderby r.Title
select r;
query = query == null ? result : query.Union(result);
}
if (searchTitle)
foreach (string word in words)
{
var result = from r in _dbConnection.GetTable<Reference>()
where r.Title.Contains(word)
orderby r.Title
select r;
query = query == null ? result : query.Union(result);
}
//...
Is there a way to structure the query in a way that results in more compact SQL?
I tried to force the creation of smaller SQL-statments by calling GetEnumerator() on the query after every loop. But apparently Union() doesn't operate on data, but on the underlying LINQ/SQL statement, so I was generating to long statements regardless.
The only solution I can think of right now, is to really gather the data after every "sub-query" and doing a union on the actual data and not in the statement. Any ideas?
For something like that, you might want to use a PredicateBuilder, as shown in the chosen answer to this question.

LINQ Aggregate vs. nested foreach

I am trying to achieve:
foreach (ScheduleItem s in ScheduleItems)
{
foreach (IScheduleModule m in s.ScheduleModules)
{
yield return m;
}
}
using LINQ aggregate and I do not understand why
return ScheduleItems.Aggregate(new Collection<IScheduleModule>(), (x, o) => x.Union(o.ScheduleModules) as Collection<IScheduleModule>);
returns null.
I have no issue using the nested foreach but my instinct was to use aggregate and I don't understand why it doesn't produce the same result.
Are there other approaches? What is best in terms of readability and performance?
You should be using SelectMany for this:
ScheduleItems.SelectMany(s => s.ScheduleModules)
That exactly matches your initial nested foreach loop. It's also equivalent to this query expression:
from s in ScheduleItems
from m in s.ScheduleModules
select m
(although that will use a slightly different form of SelectMany).
As for why Aggregate isn't working: you're calling Union which returns an IEnumerable<T>, but then using as to try to convert it to Collection<T>. The result of Union won't be a Collection<T>, hence the result of the as operator is null.
Have you tried using SelectMany? Based on your question, that sounds like what you are looking for.
var results = ScheduleItems.SelectMany(si => si.ScheduleModules);

Using LINQ to SQL and chained Replace

I have a need to replace multiple strings with others in a query
from p in dx.Table
where p.Field.Replace("A", "a").Replace("B", "b").ToLower() = SomeVar
select p
Which provides a nice single SQL statement with the relevant REPLACE() sql commands.
All good :)
I need to do this in a few queries around the application... So i'm looking for some help in this regard; that will work as above as a single SQL hit/command on the server
It seems from looking around i can't use RegEx as there is no SQL eq
Being a LINQ newbie is there a nice way for me to do this?
eg is it possible to get it as a IQueryable "var result" say and pass that to a function to add needed .Replace()'s and pass back? Can i get a quick example of how if so?
EDIT: This seems to work! does it look like it would be a problem?
var data = from p in dx.Videos select p;
data = AddReplacements(data, checkMediaItem);
theitem = data.FirstOrDefault();
...
public IQueryable<Video> AddReplacements(IQueryable<Video> DataSet, string checkMediaItem)
{
return DataSet.Where(p =>
p.Title.Replace(" ", "-").Replace("&", "-").Replace("?", "-") == checkMediaItem);
}
Wouldn't it be more performant to reverse what you are trying to do here, ie reformat the string you are checking against rather than reformatting the data in the database?
public IQueryable<Video> AddReplacements(IQueryable<Video> DataSet, string checkMediaItem)
{
var str = checkMediaItem.Replace("-", "?").Replace("&", "-").Replace("-", " "));
return DataSet.Where(p => p.Title == str);
}
Thus you are now comparing the field in the database with a set value, rather than scanning the table and transforming the data in each row and comparing that.

LINQ to SQL bug (or very strange feature) when using IQueryable, foreach, and multiple Where

I ran into a scenario where LINQ to SQL acts very strangely. I would like to know if I'm doing something wrong. But I think there is a real possibility that it's a bug.
The code pasted below isn't my real code. It is a simplified version I created for this post, using the Northwind database.
A little background: I have a method that takes an IQueryable of Product and a "filter object" (which I will describe in a minute). It should run some "Where" extension methods on the IQueryable, based on the "filter object", and then return the IQueryable.
The so-called "filter object" is a System.Collections.Generic.List of an anonymous type of this structure: { column = fieldEnum, id = int }
The fieldEnum is an enum of the different columns of the Products table that I would possibly like to use for the filtering.
Instead of explaining further how my code works, it's easier if you just take a look at it. It's simple to follow.
enum filterType { supplier = 1, category }
public IQueryable<Product> getIQueryableProducts()
{
NorthwindDataClassesDataContext db = new NorthwindDataClassesDataContext();
IQueryable<Product> query = db.Products.AsQueryable();
//this section is just for the example. It creates a Generic List of an Anonymous Type
//with two objects. In real life I get the same kind of collection, but it isn't hard coded like here
var filter1 = new { column = filterType.supplier, id = 7 };
var filter2 = new { column = filterType.category, id = 3 };
var filterList = (new[] { filter1 }).ToList();
filterList.Add(filter2);
foreach(var oFilter in filterList)
{
switch (oFilter.column)
{
case filterType.supplier:
query = query.Where(p => p.SupplierID == oFilter.id);
break;
case filterType.category:
query = query.Where(p => p.CategoryID == oFilter.id);
break;
default:
break;
}
}
return query;
}
So here is an example. Let's say the List contains two items of this anonymous type, { column = fieldEnum.Supplier, id = 7 } and { column = fieldEnum.Category, id = 3}.
After running the code above, the underlying SQL query of the IQueryable object should contain:
WHERE SupplierID = 7 AND CategoryID = 3
But in reality, after the code runs the SQL that gets executed is
WHERE SupplierID = 3 AND CategoryID = 3
I tried defining query as a property and setting a breakpoint on the setter, thinking I could catch what's changing it when it shouldn't be. But everything was supposedly fine. So instead I just checked the underlying SQL after every command. I realized that the first Where runs fine, and query stays fine (meaning SupplierID = 7) until right after the foreach loop runs the second time. Right after oFilter becomes the second anonymous type item, and not the first, the 'query' SQL changes to Supplier = 3. So what must be happening here under-the-hood is that instead of just remembering that Supplier should equal 7, LINQ to SQL remembers that Supplier should equal oFilter.id. But oFilter is a name of a single item of a foreach loop, and it means something different after it iterates.
I have only glanced at your question, but I am 90% sure that you should read the first section of On lambdas, capture, and mutability (which includes links to 5 similar SO questions) and all will become clear.
The basic gist of it is that the variable oFilter in your example has been captured in the closure by reference and not by value. That means that once the loop finishes iterating, the variable's reference is to the last one, so the value as evaluated at lambda execution time is the final one as well.
The cure is to insert a new variable inside the foreach loop whose scope is only that iteration rather than the whole loop:
foreach(var oFilter in filterList)
{
var filter = oFilter; // add this
switch (oFilter.column) // this doesn't have to change, but can for consistency
{
case filterType.supplier:
query = query.Where(p => p.SupplierID == filter.id); // use `filter` here
break;
Now each closure is over a different filter variable that is declared anew inside of each loop, and your code will run as expected.
Working as designed. The issue you are confronting is the clash between lexical closure and mutable variables.
What you probably want to do is
foreach(var oFilter in filterList)
{
var o = oFilter;
switch (o.column)
{
case filterType.supplier:
query = query.Where(p => p.SupplierID == o.id);
break;
case filterType.category:
query = query.Where(p => p.CategoryID == o.id);
break;
default:
break;
}
}
When compiled to IL, the variable oFilter is declared once and used multiply. What you need is a variable declared separately for each use of that variable within a closure, which is what o is now there for.
While you're at it, get rid of that bastardized Hungarian notation :P.
I think this is the clearest explanation I've ever seen: http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx:
Basically, the problem arises because we specify that the foreach loop is a syntactic sugar for
{
IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
try
{
int m; // OUTSIDE THE ACTUAL LOOP
while(e.MoveNext())
{
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
}
finally
{
if (e != null) ((IDisposable)e).Dispose();
}
}
If we specified that the expansion was
try
{
while(e.MoveNext())
{
int m; // INSIDE
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
then the code would behave as expected.
The problem is that you're not appending to the query, you're replacing it each time through the foreach statement.
You want something like the PredicateBuilder - http://www.albahari.com/nutshell/predicatebuilder.aspx

Resources