Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't DbSet covariant?

I have a factory function to return a DbSet(Of IItemType). The actual return type will always be an implementation IItemType, for example DbSet(Of CategoryType).

I thought covariance is supported in generics and this method would work fine, but I get an exception when I try to run my code:

Unable to cast object of type 'System.Data.Entity.DbSet1[CategoryType]' to type 'System.Data.Entity.DbSet1[IItemType]'.

like image 376
just.another.programmer Avatar asked Sep 29 '13 20:09

just.another.programmer


Video Answer


2 Answers

DbSet<T> CANNOT be co-variant. Firstly, this is because in C#, classes cannot be co-variant...only interfaces. Secondly because DbSet<T> has both co-variant and contra-variant methods.

Take the following two examples.

DbSet<CategoryType> set = context.Set<CategoryType>();
IQueryable<IItemType> query = set.Where(x => x.Foo == Bar);

So we know for a fact that all CategoryTypes are IItemType, so we know this can always work.

However conversely try this...

DbSet<CategoryType> set = context.Set<CategoryType>();
IItemType newItemType = new ProductType();
set.Add(newItemType);  // Compiler error.

Not all IItemTypes are CategoryType. So if we could cast DbSet<CategoryType> to DbSet<IItemType> we would get run time errors when adding... Since the compiler knows that this might not always work, it won't let you do the casting.

However there are interfaces that DbSet<T> does allow Co-Variance on. You can try casting it to IQueryable<IItemType> for example.

However it sounds like you are trying to query against the DbSet using a query against the interface... try the following

DbSet<CategoryType> set = context.Set<CategoryType>();
IQueryable<IItemType> cast = set.OfType<IITemType>(); //(or .Cast<>() works too)
IQueryable<IItemType> query = cast.Where(x => x ....);
like image 133
Aron Avatar answered Jan 03 '23 13:01

Aron


It looks like they could be covariant. But there is a host of differences between in-memory programming and programming against a query provider.

For Entity Framework to materialize an object from the data store it requires its exact type and the mapping from database columns to the type's properties. An interface can represent any type and EF is not (yet) able to pick a suitable implementation itself. Maybe support for interfaces will be featured in future releases.

The same applies to casting entities to an interface in an EF query (a case I just added to my list of differences).

like image 22
Gert Arnold Avatar answered Jan 03 '23 13:01

Gert Arnold