It's a question about InvalidOperationException with message Class member X is unmapped.
One of our system has the same base entity for each LinqToSql entity with framework version 3.5.
I ran into a very strange problem and I started a research about it. I made a very small project to be able to localize the problem more easily.
Entity base class
public abstract class EntityBase
{
public virtual long ID { get; set; }
}
DataContext and Entity
[Database(Name = "TestDatabase")]
public class EntitiesDataContext : DataContext
{
public EntitiesDataContext() :
base(Settings.Default.TestDatabaseConnectionString, new AttributeMappingSource())
{
}
}
[Table(Name = "dbo.MyEntity")]
public class MyEntity : EntityBase
{
private long _EntityID;
[Column(Name = "EntityID", Storage = "_EntityID")]
public override long ID
{
get { return _EntityID; }
set { _EntityID = value; }
}
[Column] public string Title;
}
The problem is the overriden ID. I made a lot of variation with different mapping attribute/property name setups, but it seems the problem is not the naming but the base class. And there is also a difference between .NET3.5 and .NET4.0.
So, for the following statements, imagine a
using (var ctx = new EntitiesDataContext())
{
//statement
}
around.
And GetTable() is GetTable<MyEntity>().
Fails means Class member EntityBase.ID is unmapped. exception. Works means the expected behavior.
1 (in 3.5) WORKS:
var result = ctx.GetTable().Where(i => i.ID == 2).FirstOrDefault();
2 (in 3.5) FAILS:
var result = ctx.GetTable().FirstOrDefault(i => i.ID == 2);
3 (in 3.5) WORKS:
var result = ctx.GetTable().FirstOrDefault(i => i.ID.Equals(2));
4 (in 3.5) WORKS:
var result = ctx.GetTable().Where(i => true).FirstOrDefault(i => i.ID == 2);
5 (in 3.5) WORKS:
var result = ctx.GetTable().Where(i => i.ID == 2).FirstOrDefault();
6 (in 4.0) FAILS:
var result = ctx.GetTable().Where(i => i.ID == 2).FirstOrDefault()
7 (in 4.0) WORKS:
var result = ctx.GetTable().Where(i => i.ID.Equals(2)).FirstOrDefault();
8 (in 4.0) FAILS (redundant with 6)
var result = ctx.GetTable().FirstOrDefault(i => i.ID == 2);
9 (in 4.0) WORKS:
var result = ctx.GetTable().FirstOrDefault(i => i.ID.Equals(2));
10 (in 4.0) WORKS:
ctx.GetTable().Where(i => true).FirstOrDefault(i => i.ID == 2);
So I cannot figure out why it fails where it fails. Especially, why this works
var result = ctx.GetTable().Where(i => true).FirstOrDefault(i => i.ID == 2);
if FirstOrDefault with predicate does not? And why Equals works where == is not.
I was looking for some Equals and == difference description, but it does not give me the answer for Where(i => true)... thing.
It seems that it's not about the query but the object initialization. Because:
in 4.0 WORKS:
var result = ctx.GetTable().Where(i => i.ID == 2).Select(i => i.Title).FirstOrDefault();
but :)
in 4.0 it's also WORKS:
var result = ctx.GetTable().FirstOrDefault();
So maybe not the object initialization?
The SQL being built by LinqToSql is the same for == and Equals. Also the same for
Where(i => true).FirstOrDefault(i => i.ID == 2)
and
FirstOrDefault(i => i.ID == 2)
There is no SQL query until FirstOrDefault, and it builds the query correctly, as it expected. Where(i => true) just continues the expression building and FirstOrDefault predicate included in the SQL queries.
I was looking for other reason in Reflector in MSIL, but found nothing special.
Any guess? :)
Thank you
####Continuation (in .NET4.0)
I set up 5 simple methods to easily check in reflector:
public void WithEquals(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().FirstOrDefault(i => i.ID.Equals(2));
}
public void WithFakeWhereAndOperator(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().Where(i => true).FirstOrDefault(i => i.ID == 2);
}
public void WithOperator(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().FirstOrDefault(i => i.ID == 2);
}
public void WithOperatorSelect(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().Where(i => i.ID == 2).Select(i => i).FirstOrDefault();
}
public void WithOperatorAndWhere(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().Where(i => i.ID == 2).FirstOrDefault();
}
WithOperator and WithOperatorAndWhere fails, but here is the MSIL and what I see:
WithOperator
.method public hidebysig instance void WithOperator(class LinqToSqlTest.EntitiesDataContext ctx) cil managed
{
.maxstack 5
.locals init (
[0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
[1] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0001)
L_0000: nop
L_0001: ldarg.1
L_0002: callvirt instance class [System.Data.Linq]System.Data.Linq.Table`1<!!0> [System.Data.Linq]System.Data.Linq.DataContext::GetTable<class LinqToSqlTest.MyEntity>()
L_0007: ldtoken LinqToSqlTest.MyEntity
L_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0011: ldstr "i"
L_0016: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_001b: stloc.0
L_001c: ldloc.0
L_001d: ldtoken instance int64 LinqToSqlTest.EntityBase::get_ID()
L_0022: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_0027: castclass [mscorlib]System.Reflection.MethodInfo
L_002c: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
L_0031: ldc.i4.2
L_0032: conv.i8
L_0033: box int64
L_0038: ldtoken int64
L_003d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0042: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_0047: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression)
L_004c: ldc.i4.1
L_004d: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_0052: stloc.1
L_0053: ldloc.1
L_0054: ldc.i4.0
L_0055: ldloc.0
L_0056: stelem.ref
L_0057: ldloc.1
L_0058: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_005d: call !!0 [System.Core]System.Linq.Queryable::FirstOrDefault<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
L_0062: pop
L_0063: ret
}
WithFakeWhereAndOperator
.method public hidebysig instance void WithFakeWhereAndOperator(class LinqToSqlTest.EntitiesDataContext ctx) cil managed
{
.maxstack 5
.locals init (
[0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
[1] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0001)
L_0000: nop
L_0001: ldarg.1
L_0002: callvirt instance class [System.Data.Linq]System.Data.Linq.Table`1<!!0> [System.Data.Linq]System.Data.Linq.DataContext::GetTable<class LinqToSqlTest.MyEntity>()
L_0007: ldtoken LinqToSqlTest.MyEntity
L_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0011: ldstr "i"
L_0016: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_001b: stloc.0
L_001c: ldc.i4.1
L_001d: box bool
L_0022: ldtoken bool
L_0027: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_002c: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_0031: ldc.i4.1
L_0032: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_0037: stloc.1
L_0038: ldloc.1
L_0039: ldc.i4.0
L_003a: ldloc.0
L_003b: stelem.ref
L_003c: ldloc.1
L_003d: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_0042: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::Where<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
L_0047: ldtoken LinqToSqlTest.MyEntity
L_004c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0051: ldstr "i"
L_0056: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_005b: stloc.0
L_005c: ldloc.0
L_005d: ldtoken instance int64 LinqToSqlTest.EntityBase::get_ID()
L_0062: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_0067: castclass [mscorlib]System.Reflection.MethodInfo
L_006c: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
L_0071: ldc.i4.2
L_0072: conv.i8
L_0073: box int64
L_0078: ldtoken int64
L_007d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0082: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_0087: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression)
L_008c: ldc.i4.1
L_008d: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_0092: stloc.1
L_0093: ldloc.1
L_0094: ldc.i4.0
L_0095: ldloc.0
L_0096: stelem.ref
L_0097: ldloc.1
L_0098: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_009d: call !!0 [System.Core]System.Linq.Queryable::FirstOrDefault<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
L_00a2: pop
L_00a3: ret
}
As I see, there is no difference between the FirstOrDefault calls, the WithFakeWhereAndOperator only 'includes' a few line about the Where statement:
And the WithEquals
.method public hidebysig instance void WithEquals(class LinqToSqlTest.EntitiesDataContext ctx) cil managed
{
.maxstack 7
.locals init (
[0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
[1] class [System.Core]System.Linq.Expressions.Expression[] CS$0$0001,
[2] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0002)
L_0000: nop
L_0001: ldarg.1
L_0002: callvirt instance class [System.Data.Linq]System.Data.Linq.Table`1<!!0> [System.Data.Linq]System.Data.Linq.DataContext::GetTable<class LinqToSqlTest.MyEntity>()
L_0007: ldtoken LinqToSqlTest.MyEntity
L_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0011: ldstr "i"
L_0016: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_001b: stloc.0
L_001c: ldloc.0
L_001d: ldtoken instance int64 LinqToSqlTest.EntityBase::get_ID()
L_0022: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_0027: castclass [mscorlib]System.Reflection.MethodInfo
L_002c: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
L_0031: ldtoken instance bool [mscorlib]System.Int64::Equals(int64)
L_0036: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_003b: castclass [mscorlib]System.Reflection.MethodInfo
L_0040: ldc.i4.1
L_0041: newarr [System.Core]System.Linq.Expressions.Expression
L_0046: stloc.1
L_0047: ldloc.1
L_0048: ldc.i4.0
L_0049: ldc.i4.2
L_004a: conv.i8
L_004b: box int64
L_0050: ldtoken int64
L_0055: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_005a: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_005f: stelem.ref
L_0060: ldloc.1
L_0061: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo, class [System.Core]System.Linq.Expressions.Expression[])
L_0066: ldc.i4.1
L_0067: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_006c: stloc.2
L_006d: ldloc.2
L_006e: ldc.i4.0
L_006f: ldloc.0
L_0070: stelem.ref
L_0071: ldloc.2
L_0072: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_0077: call !!0 [System.Core]System.Linq.Queryable::FirstOrDefault<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
L_007c: pop
L_007d: ret
}
The difference is bigger:
In WithEquals, there is an extra
[System.Core]System.Linq.Expressions.Expression[]
initialization, and it calls Equals in the middle of the method.
Also, I can see that WithOperator uses
call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression)
while WithEquals uses
call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo, class [System.Core]System.Linq.Expressions.Expression[])
This is in the line 38 on the second image.
Hm, maybe it's a problem about the difference between BinaryExpression and MethodCallExpression? I will make further research about these.
So on
We have a working
public void WithOperatorSelect(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().Where(i => i.ID == 2).Select(i => i).FirstOrDefault();
}
Same as
public void WithOperatorAndWhere(EntitiesDataContext ctx)
{
ctx.GetTable<MyEntity>().Where(i => i.ID == 2).FirstOrDefault();
}
but with an extra fake select and it works.
MSIL
WithOperatorAndSelect
.method public hidebysig instance void WithOperatorSelect(class LinqToSqlTest.EntitiesDataContext ctx) cil managed
{
.maxstack 5
.locals init (
[0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
[1] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0001)
L_0000: nop
L_0001: ldarg.1
L_0002: callvirt instance class [System.Data.Linq]System.Data.Linq.Table`1<!!0> [System.Data.Linq]System.Data.Linq.DataContext::GetTable<class LinqToSqlTest.MyEntity>()
L_0007: ldtoken LinqToSqlTest.MyEntity
L_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0011: ldstr "i"
L_0016: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_001b: stloc.0
L_001c: ldloc.0
L_001d: ldtoken instance int64 LinqToSqlTest.EntityBase::get_ID()
L_0022: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_0027: castclass [mscorlib]System.Reflection.MethodInfo
L_002c: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
L_0031: ldc.i4.2
L_0032: conv.i8
L_0033: box int64
L_0038: ldtoken int64
L_003d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0042: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_0047: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression)
L_004c: ldc.i4.1
L_004d: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_0052: stloc.1
L_0053: ldloc.1
L_0054: ldc.i4.0
L_0055: ldloc.0
L_0056: stelem.ref
L_0057: ldloc.1
L_0058: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_005d: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::Where<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
L_0062: ldtoken LinqToSqlTest.MyEntity
L_0067: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_006c: ldstr "i"
L_0071: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_0076: stloc.0
L_0077: ldloc.0
L_0078: ldc.i4.1
L_0079: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_007e: stloc.1
L_007f: ldloc.1
L_0080: ldc.i4.0
L_0081: ldloc.0
L_0082: stelem.ref
L_0083: ldloc.1
L_0084: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, class LinqToSqlTest.MyEntity>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_0089: call class [System.Core]System.Linq.IQueryable`1<!!1> [System.Core]System.Linq.Queryable::Select<class LinqToSqlTest.MyEntity, class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, !!1>>)
L_008e: call !!0 [System.Core]System.Linq.Queryable::FirstOrDefault<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>)
L_0093: pop
L_0094: ret
}
WithOperatorAndWhere
.method public hidebysig instance void WithOperatorAndWhere(class LinqToSqlTest.EntitiesDataContext ctx) cil managed
{
.maxstack 5
.locals init (
[0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
[1] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0001)
L_0000: nop
L_0001: ldarg.1
L_0002: callvirt instance class [System.Data.Linq]System.Data.Linq.Table`1<!!0> [System.Data.Linq]System.Data.Linq.DataContext::GetTable<class LinqToSqlTest.MyEntity>()
L_0007: ldtoken LinqToSqlTest.MyEntity
L_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0011: ldstr "i"
L_0016: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_001b: stloc.0
L_001c: ldloc.0
L_001d: ldtoken instance int64 LinqToSqlTest.EntityBase::get_ID()
L_0022: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_0027: castclass [mscorlib]System.Reflection.MethodInfo
L_002c: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
L_0031: ldc.i4.2
L_0032: conv.i8
L_0033: box int64
L_0038: ldtoken int64
L_003d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0042: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_0047: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression)
L_004c: ldc.i4.1
L_004d: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_0052: stloc.1
L_0053: ldloc.1
L_0054: ldc.i4.0
L_0055: ldloc.0
L_0056: stelem.ref
L_0057: ldloc.1
L_0058: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class LinqToSqlTest.MyEntity, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
L_005d: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::Where<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
L_0062: call !!0 [System.Core]System.Linq.Queryable::FirstOrDefault<class LinqToSqlTest.MyEntity>(class [System.Core]System.Linq.IQueryable`1<!!0>)
L_0067: pop
L_0068: ret
}
And the difference:
The only difference (in addition to that WithOperatorAndSelect works :) ) is the Select statement in MSIL.
So I guess it's not an == operator / Equals problem. But I don't know.
Ok, it seems that this is a known bug:
http://connect.microsoft.com/VisualStudio/feedback/details/394255/linq-to-sql-bug-handling-entities-with-common-base-class
and it won't be fixed.
Related questions:
LINQ to SQL - mapping exception when using abstract base classes
LinqToSql and abstract base classes
Anyway, thank you Frank Tzanabetis for your effort.
I'll have a stab in the dark here. Perhaps it's got something to do with == being staic and .Equals() is virtual.
Because of this, you'd end up with behaviour like so:
object x = 10;
object y = 10;
x == y // returns false as == is static and the compiler statically binds it
// to object, which uses a reference comparison, hence the false
x.Equals(y) // returns true as .Equals is virtual, thus calls the Int32
// implementation of .Equals which does a value type compare.
Perhaps linqtosql can't determine which ID you're referring to when you're using "==" - it thinks it's of the base class type, whereas with .Equals() it knows you're referring to your concrete sub class and can figure it out.
I dunno, just a guess...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With