24

I have a number of entity framework tables that I have made support an interface IHistoricEntity using their parital classes. IHistoricEntity has ActiveTo Datetime? property.

// Auto generated LINQ to Entities domain service:
[EnableClientAccess()]
public partial class ProductService : LinqToEntitiesDomainService<ProductDBEntities>
{
    public IQueryable<ANALYSIS_CODES> GetANALYSIS_CODES()
    {
        return this.ObjectContext.ANALYSIS_CODES;
    }
 ...
}

// My Partial class to add interface
public partial class ANALYSIS_CODES : IHistoricEntity
{}

I am trying to refactor this working code to a method:

List<ANALYSIS_CODE> filtered = (from rec in ps.GetANALYSIS_CODES() where rec.ActiveTo == null select rec).ToList()

Like so:

private List<T> Filter<T>(IQueryable<T> queryable) where T : IHistoricEntity
{
    return (from rec in queryable where rec.ActiveTo == null select rec).ToList();
}

// called like this:
List<ANALYSIS_CODE> filtered = Filter(ps.GetANALYSIS_CODES());

This gives this exception on the ToList:

Unable to cast the type 'ANALYSIS_CODES' to type 'IHistoricEntity'. LINQ to Entities only supports casting Entity Data Model primitive types.

But where have I asked it to cast to an IHistoricEntity? I have mearly said that T must support IHistoricEntity.

weston
  • 54,145
  • 21
  • 145
  • 203
  • Have you tried storing the query in a variable and examining the expression property of it in the debugger before ToListing it? That should show you where the cast is. – wizzardmr42 Dec 04 '12 at 10:16
  • There's nothing useful in there, and when I try to expand results I get the exception. – weston Dec 04 '12 at 10:29
  • Have you tried explicitly accessing the .expression property? It isn't always visible in the debugger, but you should be able to type it manually. Also, try query.ToString() in the debugger as that might give a clue too (works for Linq to SQL, don't think I've tried it with Entities yet) – wizzardmr42 Dec 04 '12 at 11:12

1 Answers1

46

rec.ActiveTo refers to a property defined in your interface. Therefore Linq needs to cast rec to IHistoricEntity before being able to access that property.

Don't be fooled by the exception being raised in .ToList(): the Linq query is only evaluated and executed when the records are needed, in this case, when the collection is to be transformed into a List<>.

Update: I verified @hvd's comment, and indeed, adding a where T: class clause changes the Linq expression from

System.Collections.Generic.List`1[MyType]
    .Where(x => (Convert(x).ActiveTo == Convert(null)))

to

System.Collections.Generic.List`1[MyType]
    .Where(x => (x.ActiveTo == Convert(null)))
devio
  • 36,858
  • 7
  • 80
  • 143
  • 24
    "Therefore Linq needs to cast `rec` to `IHistoricEntity` before being able to access that property." -- Almost, but not quite. That requirement is avoided by including a `class` constraint on the generic argument. When the type is known in advance to be a reference type, the conversion to `IHistoricEntity` is a no-op and won't be included. (The cast is otherwise potentially a boxing operation, because `T` could be a struct.) –  Dec 04 '12 at 11:20
  • 2
    @hvd Nice, thanks to both of you, `where T : class, IHistoricEntity` did the trick. And that makes perfect sense to boot! – weston Dec 04 '12 at 12:08
  • Wow! I was founding solution for days! And where T: class fixes it! Thanks a lot!!!1 – Alexander Vasilyev Sep 26 '14 at 05:21