在Entity Framework中包含派生类型的嵌套对象的最佳方法是什么?

时间:2015-11-05 07:33:24

标签: c# entity-framework inheritance

我需要在实体中包含嵌套对象。问题是还需要嵌套在嵌套对象中的嵌套对象,并且在它上面这些第二级嵌套对象具有不同的类型。我将展示一个类似于我的案例的例子,不要担心架构是否对你有意义,我只想尝试给出一个更简单的例子。所以:

public class Garage
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Driver> Drivers { get; set; }
}

public abstract class Vehicle<TDriver, TMechanic>
    where TDriver : Driver
    where TMechanic : Mechanic
{
    public virtual ICollection<TDriver> Drivers { get; set; }
    public virtual ICollection<TMechanic> Mechanics { get; set; }
}

public class Car : Vehicle<CarDriver, CarMechanic>
{
    // Some properties
}

public class Truck : Vehicle<TruckDriver, TruckMechanic>
{
    // Some properties
}

public abstract class Driver
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int GarageId { get; set; }
    public virtual Garage Garage { get; set; }
}

public class CarDriver : Driver
{
    public int CarId { get; set; }
    public virtual Car Car { get; set; }
}

public class TruckDriver : Driver
{
    public int TruckId { get; set; }
    public virtual Truck Truck { get; set; }
}

public abstract class Mechanic
{
    public int Id { get; set; }
}

public class CarMechanic : Mechanic
{
    public int CarId { get; set; }
    public virtual Car Car { get; set; }
}

public class TruckMechanic : Mechanic
{
    public int TruckId { get; set; }
    public virtual Truck Truck { get; set; }
}

让我们说,由于某种原因,司机可以拥有汽车或卡车,这就是这种班级结构的原因。

所以,现在,当我创建一个新的Garage实体并在其中创建新的驱动程序时,我只提供他们的汽车或卡车实体的ID。在创建具有嵌套Driver实体的新Garage实体之后,一切正常,除了Driver实体(CarDriver或TruckDriver)只加载了CarId和TruckId字段这一事实。问题是我还需要真正的汽车和卡车实体。那么,我怎么能包括它们呢?

以下行没有完成这项工作。

context.Garages.Include(g => g.Drivers) 

我想出的唯一一件事(我知道它是我能做的最愚蠢的事情)是创建DbContext对象的新实例并通过Id获取Garage实体。

有没有人知道如何在不做这种愚蠢的解决方法的情况下应对这种情况?

修改 我添加了Vehicle,Mechanic,CarMechanic和TruckMechanic类,以便我的示例可以更像我的真实代码。现在,从这个例子来看,Car对象成为CarDrivers和CarMechanics的车辆并没有多大意义。但就我而言,这是相关的。

1 个答案:

答案 0 :(得分:2)

  

我们可以说,出于某种原因,司机可以拥有一辆汽车或一辆汽车   卡车,这就是这种班级结构的原因。

     

...

     

问题是我还需要真正的汽车和卡车实体。又怎样   我可以加入吗?

您似乎希望能够以多态方式访问每个驾驶员的车辆。但显然,您没有在基础Driver课程中公开这样的工具,并且由于您使用Garage.Drivers(使用该基础Driver课程)访问您的数据,最终导致车辆无法进入。

现在,在一个理想的世界中,你只会这样做:

public abstract class Vehicle
{
    public int Id { get; set; }
    public string Make { get; set; }
}

public class Car : Vehicle { public string CarSpecificProperty { get; set; } }
public class Truck : Vehicle { public string TruckSpecificProperty { get; set; } }

public abstract class Driver<TVehicle> where TVehicle : Vehicle
{
    public virtual TVehicle Vehicle { get; set; }
}

public class CarDriver : Driver<Car> {}
public class TruckDriver : Driver<Truck> {}

问题在于,实体框架将拒绝映射开放的通用实体,您将无法在上下文中创建适当的DbSet<Driver<?>>

这将要求我们提出一种不那么清洁的方法:

public abstract class Vehicle
{
    public int Id { get; set; }
    public string Make { get; set; }
}

public class Car : Vehicle { public string CarSpecificProperty { get; set; } }
public class Truck : Vehicle { public string TruckSpecificProperty { get; set; } }

public abstract class Driver
{
    public int Id { get; set; }
    public virtual Vehicle Vehicle { get; set; }
}

public class CarDriver : Driver
{
    public Car Car
    {
        get { return this.Vehicle as Car; }
        set { this.Vehicle = value as Car; }
    }
}

public class TruckDriver : Driver
{
    public Truck Truck
    {
        get { return this.Vehicle as Truck; }
        set { this.Vehicle = value as Truck; }
    }
}

在您的DbContext

public IDbSet<Driver> Drivers { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<CarDriver>().Ignore(x => x.Car);
    modelBuilder.Entity<TruckDriver>().Ignore(x => x.Truck);

    base.OnModelCreating(modelBuilder);
}

实际用法是:

var driversWithVehicles = context.Drivers.Where(x => x.Vehicle != null);
var driversWithToyotaVehicles = context.Drivers.Where(x => x.Vehicle.Make == "Toyota");

var carDrivers = context.Drivers.OfType<CarDriver>();
var carDriversWithCriteria = context.Drivers.OfType<CarDriver>().Where(x => (x.Vehicle as Car).CarSpecificProperty == "SomeValue");

var truckDrivers = context.Drivers.OfType<TruckDriver>();
var truckDriversWithCriteria = context.Drivers.OfType<TruckDriver>().Where(x => (x.Vehicle as Truck).TruckSpecificProperty == "SomeValue");

(请注意,我们不使用Car/Truck属性访问.Car/.Truck特定数据,因为它们会被故意忽略,而EF会在尝试访问它们时抛出)

现在,要解决最后一段Garage Include问题:

context.Garages.Include(g => g.Drivers.Select(d => d.Vehicle));

或者

context.Garages.Include("Drivers.Vehicle");

当然,您现在还可以直接查询这些Drivers数据:

var allGaragesWithCars = context.Garages.Where(g => g.Drivers.OfType<CarDriver>().Any());