Code sharing options in Linq2SQL expressions
I have a couple of Linq to SQL queries that contain the same Where clause, specifically:
where ((range.MinimumFrequency <= minFreq && minFreq <= range.MaximumFrequency)
|| (range.MinimumFrequency <= maxFreq && maxFreq <= range.MaximumFrequency)
|| (range.MinimumFrequency <= minFreq && maxFreq <= range.MaximumFrequency)
|| (range.MinimumFrequency >= minFreq && maxFreq >= range.MaximumFrequency))
And instead of copying and pasting this piece of code all over the place, I would like to refactor it into something else that can be used. I know I cannot do this with the normal method as it cannot be translated to SQL, but I also cannot get
Expression <Func <... →
things described here to work.
If I am simplifying the where clause for my convenience here, I want to rotate
where (range.MinumumFrequency < minFreq)
into an expression, so I tried:
public static Expression<Func<FreqRange, bool>> Overlaps(decimal minMHz, decimal maxMHz)
{
return (range => range.MinimumFrequency <= minMHz);
}
It looks like compilation, but I can't get the where clause to work, I tried the following:
where FreqRange.Overlaps(minMHz, maxMHz)
but this gives me a compile time error:
Cannot implicitly convert type 'System.Linq.Expressions.Expression>' to 'bool'
Any ideas? Also, assuming we get this working, can I just extend the lambda expression in <Func <→ to include other conditions?
source to share
If you are using LINQ syntax with language construction, you are implicitly declaring a lamba expression - and that lamba expression is Expression<Func <>>
- so you don't need a where clause to return an expression, you need an expression.
eg.
var q = from row in myTable
where row.a < aMax
select row.b;
// which translates to
var q = myTable.Where(row => row.a < aMax).Select(row => row.b);
Now you need to "cache" row => row.a < aMax
, not just the value row.a < aMax
, if you like. So, if you were writing something like this ...
Expression<Func<?,bool>> cachedExpression = row => row.a < aMax;
var q = from row in myTable
where cachedExpression
select row.b;
Well, you say "where, given the row, it's true that (given the row, it's true that row.a is less than aMax)". This is nonsense and won't compile if cachedExpression is of type Expression<Func<?,bool>>
and myTable is a LinqToSql table. This will translate something like this:
Expression<Func<?,bool>> cachedExpression = row => row.a < aMax;
//nonsense .Where:
var q = myTable.Where(row => cachedExpression).Select(row => row.b);
//effectively says this:
var q = myTable.Where(row => (row0 => row0.a < aMax)).Select(row => row.b);
This is now legal in the sense that the linq query provider can implement whereclause with a non-boolean value, but this is very strange; Of course, no standard linq providers (such as Linq to Sql) do this.
So what should you do? Something like:
//not .Where(row=>cachedExpression)!
var q = myTable.Where(cachedExpression).Select(row => row.b);
//or, alternatively:
var q = from filteredRow in myTable.Where(cachedExpression)
select filteredRow.b;
source to share
You can also create a method that accepts and dispatches an IQueryable. I've used these views, but only in code.
public class WithProps
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public override string ToString()
{
return string.Format("{0}-{1}", Prop1, Prop2);
}
}
public static class Tools
{
public static IQueryable<WithProps> MyFilter(this IQueryable<WithProps> props)
{
return props.Where(p => p.Prop1 == "hi");
}
}
class Program
{
static void Main(string[] args)
{
var propList = new List<WithProps>()
{
new WithProps(){ Prop1="hi", Prop2="there"},
new WithProps(){ Prop1="hi", Prop2="bye"},
new WithProps(){ Prop1="hello", Prop2="world"},
new WithProps(){ Prop1="bye", Prop2="friend"}
};
var filtered = propList.AsQueryable().MyFilter();
Console.WriteLine("propList ===");
foreach (var item in propList)
Console.WriteLine(item.ToString());
Console.WriteLine("filtered ===");
foreach (var item in filtered)
Console.WriteLine(item.ToString());
}
}
... Results...
propList === hi-there hi-bye hello-world bye-friend filtered === hi-there hi-bye
source to share