【问题标题】:Multiple one-to-one relationship with two independent tables and one dependent具有两个独立表和一个依赖表的多个一对一关系
【发布时间】:2019-03-31 11:44:52
【问题描述】:

我已经阅读了很多关于这个主题的相关问题,但似乎没有一个能解决我的问题,所以请多多包涵。 我是 EF 新手,并试图在 ASP .NET MVC 中使用 EF6 建立以下关系:

我需要有两个永久表,Drivers 和 Cars。当 Driver 与 Car 关联时,我现在需要在这些表之间创建关系。 但一名司机只能分配给一辆车。

Driver 可能并不总是与 Car 相关联,反之亦然,即使它们之间并不总是关联,我也想维护这两个表,这就是为什么我认为我需要专门有一个额外的表建立这种联系。我认为这将在这些类之间创建 1:1:1 的关系。

以下是我的 POCO 课程的模型。

模型

public class Driver
{
    public int DriverID { get; set; }
    public string Name { get; set; }
    //other additional fields

    public DriverCar DriverCar { get; set; }
}

public class Car
{
    public int CarID { get; set; }
    public string Brand { get; set; }
    //other additional fields

    public DriverCar DriverCar { get; set; }
}

public class DriverCar
{
    public int DriverCarID { get; set; }

    public int DriverID { get; set; }
    public Driver Driver { get; set; }

    public int CarID { get; set; }
    public Car Car { get; set; }
 }

我曾尝试使用 Fluent API 配置关系,但我认为我做错了,因为我遇到了以下错误:

引入 FOREIGN KEY 约束“FK_dbo.DriverCar_dbo.Car_CarId” 表“DriverCar”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN 关键约束。无法创建约束或索引。见上 错误。

Fluent Api

modelBuilder.Entity<DriverCar>()
                        .HasRequired(a => a.Driver)
                        .WithOptional(s => s.DriverCar)
                        .WillCascadeOnDelete(false);

modelBuilder.Entity<DriverCar>()
                        .HasRequired(a => a.Car)
                        .WithOptional(s => s.DriverCar)
                        .WillCascadeOnDelete(false);

我真的不确定我是否遗漏了什么,或者是否有更好的方法来处理这种情况,如果有人能就如何解决这个问题给我一些反馈,我将不胜感激。


更新

刚刚在这里找到了一个有趣的答案:Is it possible to capture a 0..1 to 0..1 relationship in Entity Framework? 我相信这正是我想要的:0..1 到 0..1 的关系。但是所有提到的选项似乎都太复杂了,我不太确定哪一个是最好的,或者如何正确地实现它们。

这些类型的关系应该很难在 EF 中实现吗? 例如,我尝试了选项 1,但它从两个表中创建了 0..1 到多的关系——驾驶员到汽车和汽车到驾驶员。那么我应该如何在它们之间创建一个独特的关联呢?

【问题讨论】:

  • 我认为你把这件事复杂化了。然后,汽车 ID 应该在 Driver 表上。在两者之间添加另一个实体对于多对多关系很有用,但在这种情况下只会增加不需要的开销。 CarID 只需在 driver 表上为空,因为您并不总是有车。
  • 它最终变成了多对多的关系。一个司机可以驾驶两辆或更多辆汽车。驾驶员和汽车 ID 将在驾驶员表中重复。这种情况需要第三张表。
  • 我同意不需要 DriverCar 表。这是每个问题的一对零关系。 stackoverflow.com/questions/14701378/…
  • @MarkFitzpatrick 问题如果没有第三张表,据我了解,根据1:0..1关系的定义,如果a Car和Driver之间没有关联,则依赖表(因为你必须设置一个依赖端)行会消失。例如,如果您决定 Car 是从属表,如果一行与 Driver 没有关联,它将根本不会出现,这就是我不想要的。我需要有 Driver 和 Car 的永久表。
  • @Neil.Work 我已尝试按照您共享的链接中提供的答案进行操作,但我不断收到此错误:在类型“MyApp.Models.Car”上声明的导航属性“Driver”已配置有冲突的外键。 你知道我为什么会收到这个错误吗?

标签: c# entity-framework ef-fluent-api


【解决方案1】:

为您的模型试试这个。 Virtual 启用延迟加载,建议用于导航属性。 DataAnnotations 显示外键(或使用流利的)以确保每个关系都使用正确的键。

public class Driver
{
    public int DriverID { get; set; }
    public string Name { get; set; }
    //other additional fields

    public DriverCar? DriverCar { get; set; }
}

public class Car
{
    public int CarID { get; set; }
    public string Brand { get; set; }
    //other additional fields

    public DriverCar? DriverCar { get; set; }
}

public class DriverCar
{
    public int DriverCarID { get; set; }

    [ForeignKey("Driver")]
    public int DriverID { get; set; }
    public Driver Driver { get; set; }

    [ForeignKey("Car")]
    public int CarID { get; set; }
    public Car Car { get; set; }
 }

modelBuilder.Entity<Driver>()
                        .HasOptional(a => a.DriverCar)
                        .WithRequired(s => s.Driver)
                        .WillCascadeOnDelete(false);

modelBuilder.Entity<Car>()
                        .HasOptional(a => a.DriverCar)
                        .WithRequired(s => s.Car)
                        .WillCascadeOnDelete(false);

注意:更改为外键的数据注释。倒置的流利陈述。修复了 Driver 到 Car 在第二个关系中的问题。

【讨论】:

  • 我试过了,但它无法识别 .HasForeignKey。我收到此错误:“EntityTypeConfiguration”不包含“HasForeignKey”的定义,并且无法找到接受“EntityTypeConfiguration”类型的第一个参数的扩展方法。
  • 您的 DbContext 中确实包含所有三个类,对吧?
  • 是的,我有。
【解决方案2】:

这是一种创建 1 到 0 的简单方法。请注意,我喜欢将所有表格的 Id 保持为 Id,而不是 CarId 等,只是我的风格。这只是一个控制台应用程序,因此一旦您添加了 EF nuget,您就可以复制/粘贴。

但下面的代码适用于 .net framework 4.6 和 EF6.2 它会创建以下表格

汽车

  • Id(PK,int,非空)
  • Driver_Id (FK, int, null)

司机

  • Id(PK,int,非空)

在这种模式下,一辆汽车只能有一个司机。不过,一个司机仍然可以驾驶多辆汽车。我不确定这是否对您来说是个问题。

    using System.Data.Entity;

    namespace EFTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                var connectionString = "<your connection string>";
                var context = new DatabaseContext(connectionString);

                var car = new Car();
                var driver = new Driver();

                context.Cars.Add(car);
                context.Drivers.Add(driver);
                car.Driver = driver;

                context.SaveChanges();

            }
        }

        public class Car
        {
            public int Id { get; set; }
            public virtual Driver Driver { get; set; }
        }
        public class Driver
        {
            public int Id { get; set; }
        }

        public class DatabaseContext : DbContext, IDatabaseContext
        {
            public DbSet<Car> Cars { get; set; }
            public DbSet<Driver> Drivers { get; set; }

            public DatabaseContext(string connectionString) : base(connectionString){ }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Car>()
                    .HasKey(n => n.Id)
                    .HasOptional(n => n.Driver);

                modelBuilder.Entity<Driver>()
                    .HasKey(n => n.Id);
            }

        }
    }

但是,如果您真的想强制每个汽车和驾驶员只有一个映射的约束,您可以使用下面的代码来实现。请注意,当您拥有加入实体时,不要将其 Id 放在加入实体的任何位置。

using System.Data.Entity;

namespace EFTest
{
class Program
{
    static void Main(string[] args)
    {
        var connectionString = "your connection string";
        var context = new DatabaseContext(connectionString);

        //Create a car, a driver, and assign them
        var car = new Car();
        var driver = new Driver();
        context.Cars.Add(car);
        context.Drivers.Add(driver);
        context.SaveChanges();
        var assignment = new DriverAssignment() { Car_id = car.Id, Driver_Id = driver.Id };
        context.DriverAssignments.Add(assignment);
        context.SaveChanges();

        //Create a new car and a new assignment
        var dupCar = new Car();
        context.Cars.Add(dupCar);
        context.SaveChanges();
        var dupAssignment = new DriverAssignment() { Car_id = dupCar.Id, Driver_Id = driver.Id };
        context.DriverAssignments.Add(dupAssignment);

        //This will throw an exception because it will violate the unique index for driver.  It would work the same for car.
        context.SaveChanges();

    }
}

public class Car
{
    public int Id { get; set; }
}
public class Driver
{
    public int Id { get; set; }
}

public class DriverAssignment
{
    public int Car_id { get; set; }

    public int Driver_Id { get; set; }
}


public class DatabaseContext : DbContext, IDatabaseContext
{
    public DbSet<Car> Cars { get; set; }
    public DbSet<Driver> Drivers { get; set; }

    public DbSet<DriverAssignment> DriverAssignments { get; set; }

    public DatabaseContext(string connectionString) : base(connectionString) { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Car>().HasKey(n => n.Id);
        modelBuilder.Entity<Driver>().HasKey(n => n.Id);
        modelBuilder.Entity<DriverAssignment>().HasKey(n => new { n.Car_id, n.Driver_Id });
        modelBuilder.Entity<DriverAssignment>().HasIndex(n => n.Car_id).IsUnique();
        modelBuilder.Entity<DriverAssignment>().HasIndex(n => n.Driver_Id).IsUnique();
    }

}

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    相关资源
    最近更新 更多