EF 4.3.1 Include inherited navigation properties in LinqToEntities query
Asked Answered
M

1

4

I'm trying to setup a simple inheritance scenario with EF 4.3.1 using code first approch and fluent configuration.

I've created an abstract base type 'A' with a one-to-one navigation property and an inherited class 'AA' also with a one-to-one navigation property has following :

public abstract class A
{
    public Guid ID { get; set; }
    public B ChildB { get; set; }
}

public class AA : A
{
    public C ChildC { get; set; }
}

public class B
{
    public Guid ID { get; set; }
    public A Parent { get; set; }
}

public class C
{
    public Guid ID { get; set; }
    public AA Parent { get; set; }
}

public class AConfiguration : EntityTypeConfiguration<A>
{
    public AConfiguration()
    {
        this.HasRequired(o => o.ChildB)
            .WithRequiredPrincipal(o => o.Parent);

        this.Map(o =>
        {
            o.ToTable("A");
        });
    }
}

public class AAConfiguration : EntityTypeConfiguration<AA>
{
    public AAConfiguration()
    {
        this.HasRequired(o => o.ChildC)
            .WithRequiredPrincipal(o => o.Parent);

        this.Map(o =>
        {
            o.ToTable("AA");
        });
    }
}

public class BConfiguration : EntityTypeConfiguration<B>
{
    public BConfiguration()
    {
        this.HasRequired(o => o.Parent)
            .WithRequiredDependent(o => o.ChildB);

        this.Map(o =>
        {
            o.ToTable("B");
        });
    }
}

public class CConfiguration : EntityTypeConfiguration<C>
{
    public CConfiguration()
    {
        this.HasRequired(o => o.Parent)
            .WithRequiredDependent(o => o.ChildC);

        this.Map(o =>
        {
            o.ToTable("C");
        });
    }
}

public class DataContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add<A>(new AConfiguration());
        modelBuilder.Configurations.Add<AA>(new AAConfiguration());
        modelBuilder.Configurations.Add<B>(new BConfiguration());
        modelBuilder.Configurations.Add<C>(new CConfiguration());
    }

    public DbSet<AA> AASet { get; set; }
    public DbSet<B> BSet { get; set; }
    public DbSet<C> CSet { get; set; }
}

When I try to get my data back with the first level of navigation property, it works as expected :

... dataContext.AASet.Include("ChildB") ...

But when I try to include the navigation property of the inherited type like following :

... dataContext.AASet.Include("ChildC") ...

I get an EntityCommandCompilationException at runtime with the following inner exception message :

The ResultType of the specified expression is not compatible with the required type. The expression ResultType is 'Transient.reference[...A]' but the required type is 'Transient.reference[...AA]'. Parameter name: arguments[0]

Has anybody encountered a similar issue ?

I am probably missing something but I can't see what's wrong with this sample.

What can I do to get my model works as expected ?

Malvina answered 22/5, 2012 at 15:32 Comment(0)
B
4

No, you don't miss anything. Actually you ran into an old Entity Framework bug. Your second query can be written like this:

var result = dataContext.ASet.OfType<AA>().Include("ChildC").ToList();

(when you replace your DbSet AASet by ASet).

For this type of eager loading of one-to-one mapped children on inherited types this article applies: http://weblogs.asp.net/johnkatsiotis/archive/2010/04/28/huge-ef4-inheritance-bug.aspx

The bug has been reported long time ago here: https://connect.microsoft.com/VisualStudio/feedback/details/544639/ef4-inheritance-defined-using-queryview-doesnt-work-properly-with-association

The bug still exists in EF 4.3.1. But Microsoft has announced in this thread that the bug is fixed in .NET 4.5 ( = EF 5.0).

The code would work if the relationship is one-to-many instead of one-to-one. Lazy or explicit loading would work as well (also with one-to-one relationship), I believe:

var result = dataContext.ASet.OfType<AA>().ToList();
foreach (var item in result)
    dataContext.Entry(item).Reference(a => a.ChildC).Load();

But this will generate multiple queries. If you don't have performance problems with multiple queries I would prefer the last workaround - until you can migrate to EF 5.0.

Brightman answered 22/5, 2012 at 18:5 Comment(1)
Thanx for your reply Sauma, but your first workaround generate the same exception and using an explicit load is not an option for my project.Malvina

© 2022 - 2024 — McMap. All rights reserved.