1

I'm trying to filter a LINQ-to-entities query in a generic way, but I keep getting an error. Here is a piece of code:

private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
    if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
    {
        var deletableEntities = (IQueryable<IDeletable>)entities;
        deletableEntities = deletableEntities.Where(entity => !entity.Deleted);
        entities = (IQueryable<T>)deletableEntities;
    }
    return entities;
}

Basically I'm trying to filter out deleted entities (i.e. 'Deleted' field is 'true'), if and only if the entity is IDeletable (i.e. it has the 'Deleted' field). The problem is that I can't cast IQueryable< IDeletable > back to IQueryable< T >.

Any ideas on how to fix this? And before you ask: yes, this method has to be generic.

Thanks in advance!

Paul
  • 105
  • 1
  • 12

3 Answers3

1

But you can use Cast<T>() to convert it.

 entities = deletableEntities.Cast<T>();

You could also use it to case to IDeletable as well, for example,

private IEnumerable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
    if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
    {
        return entities.ToList()
                       .Cast<IDeletable>()
                       .Where( e => !e.Deleted )
                       .Cast<T>();
    }
    return entities.ToList();
}
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • This is the same as casting to IDeletable in the Where clause (something like "return entities.Where(e => !((IDeletable)e).Deleted)"). Unfortunately it fails at runtime with error message: "LINQ to Entities only supports casting Entity Data Model primitive types". – Paul Nov 19 '11 at 20:43
  • The problem is that you're deferring the translation to the DB. You'll need to perform the query (i.e., use ToList()) before doing the first Cast. You might as well return an `IEnumerable`, too. If you need to have the translation deferred then you'll need to have two separate methods with a constraint as in `sq33G`'s answer so that you don't need to do the cast. – tvanfosson Nov 19 '11 at 20:53
  • Wouldn't the call to the second method fail at compile time? T just passed a runtime check for implementing IDeletable, but the compiler won't notice that. – sq33G Nov 19 '11 at 21:27
  • Yeah, that's the problem with writing code in a web editor. I'll have to think about it. – tvanfosson Nov 19 '11 at 21:34
  • After thinking about it I'm not sure that there is a good solution to this other than doing the deleted filter in memory as I've shown in my current example. The drawback to this is that you'd have to have it as the last filter in your chain so that you get as much done in the DB as possible before materializing the query. To that end, I've changed it to return an `IEnumerable` to make it clearer that anything after this will be done in memory. I've removed the (nonworking) code. – tvanfosson Nov 19 '11 at 21:45
  • I solved the problem by using ".Cast()" on the failing cast. For more information, please take a look at the accepted solution. Thanks! – Paul Nov 21 '11 at 15:38
1

I was able to solve my problem by doing this:

private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
    if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
    {
        var deletableEntities = (IQueryable<IDeletable>)entities;
        return deletableEntities.Where(entity => !entity.Deleted).Cast<T>();
    }
    return entities;
}

Thanks to tvanfosson for the inspiration.

Paul
  • 105
  • 1
  • 12
  • I don't understand how this could possibly work as you are casting a IQueryable to an IQueryable ... this is likely never possible – War Sep 21 '16 at 10:02
0

If you can assume that no one will need to call this method with T that does not implement IDeletable, you can restrict T:

private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities) where T : IDeletable

As a bonus, you won't need to cast anything or use reflection to test for IDeletable.

sq33G
  • 3,320
  • 1
  • 23
  • 38
  • 1
    Awfully stupid idea won't work. Anyone who guessed what it is can see why not [here](http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx). – sq33G Nov 19 '11 at 21:24