Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interesting Linq to SQL common base class behavior

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:

WithFakeWhereAndOperator and WithOperator difference

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:

WithOperator and WithEquals difference

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:

WithOperatorAndSelect and WithOperatorAndWhere 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.

like image 340
Peter Porfy Avatar asked Oct 06 '11 10:10

Peter Porfy


2 Answers

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.

like image 68
Peter Porfy Avatar answered Nov 15 '22 16:11

Peter Porfy


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...

like image 21
Frank Tzanabetis Avatar answered Nov 15 '22 17:11

Frank Tzanabetis