简体   繁体   中英

Querying by base type in EF4 Code-First

I have the following types:

public abstract class Vehicle {
    public int Id { get; set; }
    public double TopSpeed { get; set; }
}

public class Car : Vehicle {
    public int Doors { get; set; }
}

public class Motorcycle : Vehicle { 
    public string Color { get; set; }
}

And I have a code-first DBContext:

public MyDbContext: DbContext { 
    public DbSet<Car> Cars { get; set; }
    public DbSet<Motorcycle> Motorcycles { get; set; }
}

This works great if I query for a car or moto directly...

var dbContext = new MyDbContext();
var cars = dbContext.Set<Car>().Where(x=> x.TopSpeed>10); // <-- THIS WORKS

But if I want a list of all vehicles, whether car or moto, I would like to do this:

var dbContext = new MyDbContext();
var vehicles = dbContext.Set<Vehicle>().Where(x=> x.TopSpeed>10); // <-- THIS DOES NOT WORK

When I try the above code, I get an exception:

System.InvalidOperationException : The entity type Vehicle is not part of the model for the current context.

That makes perfect sense... I didn't add Vehicle to the context. I added car and moto. At this point, I'm not sure what to do. I tried adding Vehicle to my context, but that combined the tables for car and moto into one Vehicle table. I definitely want a separate table for cars and motos (and possibly a table for the vehicle base properties as well). What's the best way to do this?

Have a Vehicles Properties of type DBSet of Vehicle in your MyDbContext class.

public MyDbContext: DbContext { 
    public DbSet<Car> Cars { get; set; }
    public DbSet<Motorcycle> Motorcycles { get; set; }
    public DbSet<Vehicle> Vehicles { set; get; }
}

Now you can access all vehicles with the criteria like this

var vehicles = dbContext.Set<Vehicle>().Where(x=> x.TopSpeed>10);

Keep in mind that you should have a Key Property in your entity classes ( ID or {ClassName}ID ). Otherwise it is going to give you a run time error! (Yes the code will compile.)

public abstract class Vehicle
{
    public int ID { set; get; }
    public double TopSpeed { get; set; }
}

EDIT : As per the comment

By Default Entity Framework will do a Table Per Hierarchy .All Data in the hierarchy is saved in a single table and it use the Discriminator column to identify which record belongs to which subtype. So As the result you will have one Vehicle table with columns same as properties of All your classes in the hierarchy with an extra column called " Discriminator ". For a Record for Car, It will have the "Car" value inside the Discriminator column.

在此输入图像描述

If you want to create Single table per each type, We will go for Table Per Type . Entity Framework will create a Table for the base class and seperate table for all child classes.

To make this happen, You can use Fluent API to override the configuration.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Car>().ToTable("Cars");
        modelBuilder.Entity<Motorcycle>().ToTable("Motorcycles");

        base.OnModelCreating(modelBuilder);
    }

And the output is

在此输入图像描述

Now You should be able to query Your Vehicle Entries like this

 var vehicles = dbContext.Set<Vehicle>().Where(x => x.TopSpeed > 150).ToList();

And the result is here

在此输入图像描述

Notice that the result contains both MotorCycle type and Car type.

Check this link to decide what Inheritance Strategy to use.

Have you tried giving Vehicle its own context with just a DBSet? I think that might do it for you.

在继承的类上使用DataAnnotation.Schema.Table(“TableName”)条目,触发为继承类型(table-per-type)创建新表,并消除父类型表中的Discriminator字段,而不是需要Fluent API代码。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM