【问题标题】:Entity Framework code first double FK as PKEntity Framework 代码先将 FK 加倍为 PK
【发布时间】:2020-01-14 23:05:02
【问题描述】:

我需要先使用 Fluent API 使用 Fntity Framework 代码映射一些实体。

我有一个Driver 和一个RacingVehicle。我需要一个Driver 只在一个RacingVehicle 中,一个RacingVehicle 只在一个Driver 中。

Booth 类指的是连接它们的Contract(关联类?)。 Contract 显示了一些额外信息:Driver 和他的RacingVehicle 之间的合同将持续多少年。

我当前的代码:

public class Driver
{
    public Guid Id { get; protected set; }
    public string Name { get; protected set; }
    // others properties
    public virtual Contract Contract { get; protected set; }
}

public class RacingVehicle
{
    public Guid Id { get; protected set; }
    public int Number { get; protected set; }
    public virtual Team Team { get; protected set; }
    // others properties
    public virtual Contract Contract { get; protected set; }
}

public class Contract
{
    public virtual Driver Driver { get; protected set; }
    public virtual RacingVehicle RacingVehicle { get; protected set; }
    public int SeasonsRemaining { get; protected set; }
}

我将从Driver 访问RacingVehicle,仅通过Contract(反之亦然)。像这样的:

var teamName = ayrtonSenna.Contract.RacingVehicle.Team.Name;
var driverName = mcLaren.Contract.Driver.Name;

我将有一个 ContractManager 服务类来管理这些合同。

我相信 Contract 类必须映射为 PK,来自 DriverRacingVehicle 的 FK,对吧?

那么,我该如何映射呢?

【问题讨论】:

    标签: c# entity-framework orm ef-code-first ef-fluent-api


    【解决方案1】:

    您应该能够在配置DBContext 时进行设置(请参阅the documentation for details)。

    简而言之,鉴于您的班级...

    public class Contract
    {
        public virtual Driver Driver { get; protected set; }
        public virtual RacingVehicle RacingVehicle { get; protected set; }
        public int SeasonsRemaining { get; protected set; }
    }
    

    ..您应该能够根据以下模式指定复合键:

    class YourContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Contract>()
                .HasKey(c => new { c.Driver, c.RacingVehicle });
        }
    
        // ...
    }
    

    但是请注意,这指的是对象 DriverRacingVehicle,而不是像 stringint 这样的简单值,所以我不完全确定它是如何工作的(它一直自从我与 EF 合作以来已经有一段时间了)。

    如果这是个问题,我可以想出两个替代方案来尝试:要么直接引用每个对象的 Id 字段:

    modelBuilder.Entity<Contract>()
                .HasKey(c => new { c.Driver.Id, c.RacingVehicle.Id });
    

    ...或者将字段DriverIdRacingVehicleId添加到Contract类中,直接使用:

    modelBuilder.Entity<Contract>()
                .HasKey(c => new { c.DriverId, c.RacingVehicleId });
    

    另一种选择是为Contract 添加一个新的单独的Id,并将其用作主键。从架构的角度来看,从长远来看,这可能是一个更好的解决方案;它将更多关注点(驾驶员和车辆)彼此分开,并支持例如未来的情况,您希望一辆车有两个司机(一个备用司机?)。

    这还可以让您随时间推移保留驾驶员与车辆关系的历史记录(只需为每份合同添加开始日期和结束日期)。您当前的解决方案也可以支持这一点,但是每个驾驶员一次只能驾驶一辆车;你永远不能给同一个司机一份新合同而不改变汽车。

    缺点是您必须实现显式逻辑来检查驾驶员与车辆的关系,但这不应该太难,而且对于其他好处来说可能是值得的。

    【讨论】:

    • “为合同添加一个新的单独ID”:我想过。有时候我喜欢尝试最努力的方式,确实,学习不同的场景,呵呵“从架构的角度来看,从长远来看是一个更好的解决方案:你完全正确。但是有一段时间, “合同”仅用于知道车辆中的驾驶员。我可以将属性 Driver 放在 Vehicle 类中,并且 ContractManager 将确保 Vehicle-Driver 是唯一的和/或 Driver 仅在单个车辆中运行...但是我需要“SeasonsRemaining”信息。所以我决定这样做。谢谢答案,我会在这里尝试。
    • @MaxDolabella 好吧,如果您想以这种方式简化它,为什么不一路添加CurrentDriverCurrentDriverLastSeason(提示:使用日期而不是int;那样它会明年仍然正确,或者后年不需要更新)?否则,如果您打算通过使用单独的合同类来“改进”设计,为什么不立即以更完整的方式进行呢?然后,您将能够支持每辆车、每位司机以及历史记录的多个合同。
    【解决方案2】:

    您通常会在上下文类的 OnModelCreating() 方法中像这样配置复杂的主键,如下所示:

    public class Contract
    {
        public int DriverId { get; set; }
        public virtual Driver Driver { get; set; }
        public int RacingVehicleId { get; set; }
        public virtual RacingVehicle RacingVehicle { get; set; }
        public int SeasonsRemaining { get; set; }
    }
    
    public class RacingContext : DbContext
    {
        public DbSet<Driver> Drivers { get; set; }
        public DbSet<RacingVehicle> RacingVehicles { get; set; }
        public DbSet<Contract> Contracts { get; set; }
    
        public override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Contract>().HasKey(contract => new { contract.DriverId, contract.RacingVehicleId });
        }
    }
    

    但是,这意味着您不能添加重复的条目。因此,如果车手“Bob”与 McLaren 签订了一份合同,那么当合同结束时,他又与 McLaren 签订了另一份合同,您必须删除或覆盖旧合同,以便存储新合同的详细信息.如果你不关心历史,只对当前合约感兴趣,那么这个设置就可以了。如果你想要完整的历史记录,你必须给每个合约一个唯一的 id,或者添加 code 来检查你的合约没有重叠。您不能仅通过主键轻松执行此操作。

    【讨论】:

    • “你必须删除旧合同” 是的。这正是这个想法。我只会在 DB 中拥有当前的合同。我会在这里试试。谢谢。
    猜你喜欢
    • 2018-12-29
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2012-08-22
    • 1970-01-01
    相关资源
    最近更新 更多