24

I have a LINQ to entities model with Table Per Hierarchy inheritance. I have a query over the base type, and I want to do specific type-dependent logic. For example:

IQueryable<BaseType> base = ...

// this works fine
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList();

// this doesn't compile to SQL
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList();

Is there any way to do something like this without processing IQueryables of each derived type separately:

// I'd rather not do this:
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty);
var resultB = base.OfType<DerivedType2>().Select(d => default(int?));
var result = resultA.Concat(resultB).ToList();
ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152

5 Answers5

31

Direct casting to an entity type like (DerivedType1)b isn't supported with LINQ-to-Entities but the as operator (b as DerivedType1) is, hence you could try:

var result2 = base
    .Select(b => b is DerivedType1
        ? (b as DerivedType1).DerivedProperty
        : null)
    .ToList();
Slauma
  • 175,098
  • 59
  • 401
  • 420
  • This does seem to work, although I remember trying this before posting so maybe it only works in EF5+? – ChaseMedallion Jul 23 '13 at 19:32
  • 3
    @ChaseMedallion: Possibly, yes. I believe I've never used this before EF 5, but I'm not sure. BTW: In the meantime I discovered that if `DerivedProperty` is nullable the ternary operator is not needed, you can just use: `Select(b => (b as DerivedType1).DerivedProperty)`. If `b` is not of type `DerivedType1` EF won't throw an exception but just return `null` automatically. – Slauma Jul 23 '13 at 19:43
  • Right. EF does null-coalescing unless you are pulling a null into memory as a non-nullable type. – ChaseMedallion Jul 24 '13 at 19:22
  • 1
    @Slauma Adding to what you wrote: For non-nullable types, it appears like you can simply cast to nullable. For example, assuming `DerivedProperty` is an `int`: `Select(b => (int?) (b as DerivedType1).Derive‌​dProperty)` – Zero3 Aug 17 '16 at 09:52
1
OfType<DerivedType1>() 

will return an IEnumerable, if possible, try to change to base-type to IEnumerable instead of IQueryable, you might en up in some SQL restrictions when using IQueryable.

That is of course if you are not actually quering a database?

1

You can use EntityFramework.Extended to improve the performance of the query instead of doing 2 round trips to DB.

var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future();
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future();
var result = resultA.Concat(resultB).ToList();

In this case only one round trip to bd is executed. This framework is very useful for many other things int EF

LarsTech
  • 80,625
  • 14
  • 153
  • 225
Miguel
  • 3,786
  • 2
  • 19
  • 32
0

You could have a method on your base type that's overridden in your derived types to provide the relevant property value.

public class MyBaseClass
{
    public virtual int GetSomething()
    {
        throw new NotImplementedException();
    }
}

public class MyDerivedClass1 : MyBaseClass
{
    public int SomeProperty { get; set; }

    public override int GetSomething()
    {
        return this.SomeProperty;
    }
}

public class MyDerivedClass2 : MyBaseClass
{
    public int SomeOtherProperty { get; set; }

    public override int GetSomething()
    {
        return this.SomeOtherProperty;
    }
}

Then you could:

var result = base.Select(b => b.GetSomething()).ToList();
PeteGO
  • 5,597
  • 3
  • 39
  • 70
  • 1
    This won't work with LINQ to Entities - it will require .ToList() first to bring entities into memory, which, since only one property per entity is required by query, is highly inefficient. – Danny Varod Aug 31 '12 at 11:59
0

Try this, I have never done anything with needing to do this kind of this but this should do it. Also if you use base, first of all don't because it is a keyword but if you must, use @base the @ in front of the name denotes that it is not used as a keyword.

var resultA = base.Select(aVar =>
                            (aVar is DerivedType1) ?
                                (DerivedType)(((DerivedType1)aVar).DerivedProperty)
                                :
                                (DerivedType)(default(int?))
                        ).ToList();
Joshua G
  • 2,086
  • 3
  • 19
  • 22