实体框架代码首先将FK双重加倍为PK

时间:2019-09-13 04:43:54

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

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

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

展位类引用连接它们的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; }
}

我将仅通过RacingVehicleDriver访问Contract(反之亦然)。像这样:

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

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

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

那么,我该如何映射呢?

2 个答案:

答案 0 :(得分:1)

通常,您可以在上下文类的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 });
    }
}

但是,这意味着您不能添加重复的条目。因此,如果驾驶员“鲍勃”与迈凯伦签订了合同,那么当合同终止时,他又与迈凯伦又获得了另一份合同,则您必须删除旧合同或将其覆盖,以便存储新合同的详细信息。如果您不关心历史,仅对最新合同感兴趣,那么可以进行此设置。如果需要完整的历史记录,则必须为每个合同指定唯一的ID,或添加 code 以检查合同是否重叠。您不能仅通过主键轻松实施该操作。

答案 1 :(得分:0)

在配置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 });

另一种选择是为Id添加一个新的Contract,并将其用作主键。从架构的角度来看,从长远来看,这可能是一个更好的解决方案。它将关注点(驾驶员和车辆)彼此分离,并会支持在未来的情况下,您需要一辆车有两个驾驶员(后备驾驶员?)。

这还可以让您保留一段时间内的驾驶员与车辆的关系历史记录(只需为每个合同添加起始日期和截止日期)。您当前的解决方案也可以提供支持,但是每个驾驶员只能驾驶一辆车辆一次。您必须在不更换汽车的情况下再给同一个驾驶员新的合同。

不利之处在于,您将必须实施显式逻辑来检查驾驶员与车辆之间的关系,但这不应太难,并且可能值得其他好处。