EF使用Fluent API的外键

15

这里是我的模型。我对车辆和司机进行了一对一的映射。我将首先创建车辆,然后将司机映射到车辆上。

public class Driver
{
    public int Id { get; set; }
    public String Name { get; set; }
    public int VehicleId { get; set; }
    public virtual Vehicle Vehicle  { get; set; }
}

public class Vehicle
{  
    public int Id { get; set; }
    public String Name { get; set; }

    public virtual Driver Driver { get; set; }

    public int VehicleGroupId { get; set; }
    public virtual VehicleGroup Vehicles { get; set; }
}

我想在Driver类中使用VehicleId属性来记录司机所驾驶的车辆的ID。

我编写了以下Fluent API代码:

modelBuilder.Entity<Vehicle>()
            .HasRequired(d => d.Driver)
            .WithRequiredPrincipal();

但是它会在Drivers表中创建一个新的列-Vehicle_VehicleId,并将其映射到Vehicle表上的VehicleId。我希望Driver表中的VehicleId也能够映射。

另外,我对EF和Fluent API都很陌生。在选择WithRequiredDependent和WithRequiredPrincipal之间感到非常困惑。如果您能用简单的话来描述一下,我会很感激。谢谢。


4
你可以把从属表和主表看作是实体框架在问你:“如果你想插入这些表的记录,我应该先插入哪个表,A表还是B表?”如果你选择A表作为主表,那么B表将包含对A表的外键。顺便说一下,流畅API有很好的C#文档,你应该仔细阅读"WithRequiredDependent"和"WithRequiredPrincipal"的描述。 - Moeri
2个回答

25
这一行代码:

public int VehicleId { get; set; }

告诉 EF(Entity Framework,一种编程工具),通过编码惯例,你希望在 Driver 表中加入一个指向 Vehicle 的外键。

下面这一行代码告诉 EF 你希望从 DriverVehicle 建立一个 1:1 的关系:

public virtual Vehicle Vehicle { get; set; }

你应该删除这两行代码并使用 Fluent API 进行配置。
关于 WithRequiredPrincipalWithRequiredDependent
你正在指定 VehicleDriver 之间的强制性关系,并从 Vehicle 导航到 Driver,因此: Vehicle 1 --> 1 Driver
(因为导航属性位于“车辆”中并指向“驾驶员”,所以“车辆”是主要的,“驾驶员”是从属的。)
modelBuilder.Entity<Vehicle>()
            .HasRequired(d => d.Driver)
            .WithRequiredDependent();

你正在指定一个强制关系,使得VehicleDriver之间存在导航,因此:Vehicle 1 <-- 1 Driver
Vehicle是从属方,Driver是主导方,因为导航属性位于Driver,指向Vehicle。)
这两者是类比的:
modelBuilder.Entity<Vehicle>()
            .HasRequired(v => v.Driver)
            .WithRequiredPrincipal();

modelBuilder.Entity<Driver>()
            .HasRequired(d => d.Vehicle)
            .WithRequiredDependent();

@jnovo "你应该删除两者,坚持使用你的Fluent API配置。" 如果这样做,那么你怎么能使用“HasRequired(v => v.Vehicle)”而没有对Vehicle的引用呢? - Rob Washington
在他的流畅API配置中,@RobWashington使用了Vehicle的Driver属性,而不是反过来;你所提到的我回答中的最后一行代码只是一个替代性的、类似的配置示例,为了完整起见。 - jnovo

3

由于您的Driver实体上有VehicleIdVehicle,因此EF会创建Vehicle_VehicleId列。

请从Driver实体中删除VehicleIdVehicle

public class Driver
{
    public int Id { get; set; }
    public String Name { get; set; }
}

public class Vehicle
{  
    public int Id { get; set; }
    public String Name { get; set; }
}

使用:

modelBuilder.Entity<Vehicle>()
        .HasRequired(d => d.Driver)
        .WithRequiredPrincipal();

您正在设置关系,因此不需要在实体类中包含手动属性。

您可以从导航属性Vehicle获取VehicleId

IQueryable<int> vehicleIds = context.Drivers.Select(x => x.Id == 123).Vehicles.Id;

我已经将其删除,但它仍然创建Vehicle_VehicleId。而不是驾驶员表中的VehicleId。 - Ashish Charan
这是因为您正在使用流畅的API来定义关系。我已经更新了我的答案。也要删除虚拟属性。 - Sam Leach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接