什么';这是在实体框架中包含派生类型的嵌套对象的最佳方式

本文关键字:类型 包含 派生 嵌套 方式 最佳 对象 框架 实体 什么 | 更新日期: 2023-09-27 18:20:49

我需要在实体中包含嵌套对象。问题是,还需要嵌套在嵌套对象中的嵌套对象,最重要的是,这些二级嵌套对象具有不同的类型。我将展示一个类似于我的例子,不要担心这个模式对你来说是否有意义,我只是想举一个更简单的例子。因此:

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实体并在其中创建新的Drivers时,我只提供他们的Car或Truck实体的Id。创建了带有嵌套Driver实体的新Garage实体后,除了Driver实体(CarDriver或TruckDriver)只加载了其CarId和TruckId字段之外,一切都正常。问题是,我还需要真正的汽车和卡车实体。那么,我该如何将它们包括在内呢?

下面的行不起作用。

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

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

有人知道如何在不采取这种愚蠢的变通方法的情况下应对这种情况吗?

编辑:我添加了Vehicle、Mechanic、CarMechanic和TruckMechanic类,这样我的示例可以更类似于我的真实代码。现在,从这个例子来看,Car对象是CarDrivers和CarMechanics的车辆没有多大意义。但就我而言,这是相关的。

什么';这是在实体框架中包含派生类型的嵌套对象的最佳方式

假设出于某种原因,驾驶员可以拥有汽车或卡车,这就是这个类结构的原因。

问题是,我还需要真正的汽车和卡车实体。那么,如何我能把它们包括在内吗?

你似乎希望能够以多态的方式访问每个驾驶员的车辆。但显然,您没有在基本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在尝试访问它们时会抛出)

现在,要解决最后一个问题,即您的原始GarageInclude问题:

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());