If I have a variable of type IQueryable<T>
I have four extension methods for Where
in namespace Systm.Linq
available:
public static IQueryable<T> Where<T>(this IQueryable<T> source,
Expression<Func<T, bool>> predicate);
public static IQueryable<T> Where<T>(this IQueryable<T> source,
Expression<Func<T, int, bool>> predicate);
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T, bool> predicate);
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T, int, bool> predicate);
(The last two because IQueryable<T>
inherits from IEnumerable<T>
.)
If I use a variable of type ObjectQuery<T>
(in namespace System.Data.Objects
) I have five overloads of Where
available, namely the four above (because ObjectQuery<T>
implements IQueryable<T>
and IEnumerable<T>
among other interfaces) and in addition an instance method of this class:
public ObjectQuery<T> Where(string predicate,
params ObjectParameter[] parameters);
If I do the same programming mistake while using either IQueryable<T>
or ObjectQuery<T>
I get very different compiler errors. Here is an example program (standard C# console application template in VS2010 SP1 + System.Data.Entity.dll
assembly added to project references, the compiler error is in comment below the four examples):
using System.Data.Objects;
using System.Linq;
namespace OverloadTest
{
public class Test
{
public int Id { get; set; }
}
class Program
{
static void Main(string[] args)
{
IQueryable<Test> queryable = null;
ObjectQuery<Test> objectQuery = null;
var query1 = queryable.Where(t => t.Name == "XYZ");
// no definition for "Name" in class OverloadTest.Test
var query2 = queryable.Where(t => bla == blabla);
// "bla", "blabla" do not exist in current context
var query3 = objectQuery.Where(t => t.Name == "XYZ");
// Delegate System.Func<Overload.Test,int,bool>
// does not take 1 argument
var query4 = objectQuery.Where(t => bla == blabla);
// Delegate System.Func<Overload.Test,int,bool>
// does not take 1 argument
}
}
}
"Squiggles" look different as well in the compiler:
I understand the first two errors. But why does the compiler apparently want to use the overload number 4 (with the Func<T, int, bool> predicate
) in the last two examples and doesn't tell me that "Name" isn't defined in class Test
and that "bla" and "blabla" do not exist in the current context?
I had expected that the compiler can safely rule out overload number 5 (I don't pass in a string
as parameter) and overload number 2 and 4 (I don't pass in a lambda expression with two parameters (t,i) => ...
) but my expectation doesn't seem to be correct.
As a side note: I came across this problem when looking at this question. The questioner said there that the fourth query in the question does not compile (it has exactly the compiler error in example number 3 and 4 above), but this query is exactly the solution to his problem and to me it seems that something (a variable or property name?) is written wrong in the query (he didn't confirm this though) but this compiler error doesn't give a helpful indication what is wrong.
Edit
Refering to Martin Harris' very helpful comment below:
In example query4
the error "Delegate System.Func does not take 1 argument" is the error shown in the tooltip window when I hover over the squiggle line. In the compiler output window there are actually four errors in this order:
- Delegate System.Func does not take 1 argument
- "lambda expression" cannot be converted to "string" because "string" is not a delegate type
- The name "bla" does not exist in the current context
- The name "blabla" does not exist in the current context
But why doesn't the compiler complain with the first error for the first two examples that use IQueryable<T>
?
var query3 = objectQuery.Where((t,i) => t.Name == "XYZ");
the error will be Delegate 'System.Func<OverloadTest.Program.Test,bool>' does not take 2 arguments. Seems like the compiler tries all overloads and throw out only one error message, perhaps the last method it have tried... who knows? – DetrimentalIQueryable
variable like @Pueblo did. – DetrimentalFunc<Overload.Test,int,bool>
as valid candidate for overload, perhaps because it cannot resolve the result type of the lambda expression. But it is not clear why it consider that method as a valid candidate for ObjectQuery but not IQueriable. – Detrimental