Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework doesn't query derived classes - Error in DbOfTypeExpression

I have a base class and two derived classes.

Each of the derived classes implements the same type as a property - the only difference is the property name.

Sadly I don't have much influence on the class design -> they have been generated from a wsdl file.

I then have a property on the BaseType to encapsulate the common property. The plan was to use this property in my web views etc.

I have used the famous "Fruit-Example" to demonstrate the problem:

 public class FruitBase
    {
        public virtual int ID { get; set; }


        //
        // The plan is to use this property in mvc view
        //
        [NotMapped]
        public virtual FruitnessFactor Fruitness
        {
            get
            {
                if (this.GetType().BaseType == typeof(Apple))
                    return ((Apple)this).AppleFruitness;
                else if (this.GetType().BaseType == typeof(Orange))
                    return ((Orange)this).OrangeFruitness;
                else
                    return null;
            }
        }
    }

public class FruitnessFactor { }

In my MVC controller, the following query works absolutely fine:

return View(context.FruitEntities
                           .OfType<Apple>().Include(a =>a.AppleFruitness)
                           .ToList());

But this one doesn't:

  return View(context.FruitEntities
                                   .OfType<Apple>().Include(a =>a.AppleFruitness)
                                   .OfType<Orange>().Include(o => o.OrangeFruitness)
                                   .ToList());

The error message I get is:

DbOfTypeExpression requires an expression argument with a polymorphic result type that is compatible with the type argument.

I am using EF 5.0 RC and the Code First approach.

Any help is much appreciated!

like image 359
Flo Avatar asked Jun 14 '12 08:06

Flo


1 Answers

As far as I can tell you can't apply Include on multiple subtypes in a single database query. You can query one type (OfType<Apple>().Include(a => a.AppelFruitness)) and the same for another subtype. The problem is that you can't concat the results in the same query because the result collections have different generic types (apples and oranges).

One option would be to run two queries and copy the result collection into a new collection of the base type - as you already indicated in the comment section under your question.

The other option (which would only need a single query) is a projection. You would have to define a projection type (you could also project into an anonymous type)...

public class FruitViewModel
{
    public FruitBase Fruit { get; set; }
    public FruitnessFactor Factor { get; set; }
}

...and then can use the query:

List<FruitViewModel> fruitViewModels = context.FruitEntities
    .OfType<Apple>()
    .Select(a => new FruitViewModel
    {
        Fruit = a,
        Factor = a.AppleFruitness
    })
    .Concat(context.FruitEntities
    .OfType<Orange>()
    .Select(o => new FruitViewModel
    {
        Fruit = o,
        Factor = o.OrangeFruitness
    }))
    .ToList();

If you don't disable change tracking (by using AsNoTracking) the navigation properties get populated automatically when the entities get attached to the context ("Relationship fixup") which means that you can extract the fruits from the viewModel collection...

IEnumerable<FruitBase> fruits = fruitViewModels.Select(fv => fv.Fruit);

...and you'll get the fruits including the FruitnessFactor properties.

This code is pretty awkward but a direct approach without using a projection has been asked for several times without success:

  • bottleneck using entity framework inheritance
  • Entity Framework - Eager loading of subclass related objects
  • How do I deeply eager load an entity with a reference to an instance of a persistent base type (Entity Framework 4)
like image 93
Slauma Avatar answered Oct 31 '22 12:10

Slauma