具有两个独立表和一个从属关系的多个一对一关系

时间:2018-10-26 17:05:16

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

我已经阅读了很多与此主题相关的问题,但是似乎都没有解决我的问题,因此请耐心等待。 我是EF的新手,并尝试使用EF6在ASP .NET MVC中建立以下关系:

我需要有两个永久性表,Drivers和Cars。现在,当驾驶员与汽车相关联时,我需要在这些表之间创建关系。 但是只能将一名驾驶员分配给一辆汽车。

驾驶员不一定总是与汽车相关联,反之亦然,即使它们之间并不总是具有关联性,我也希望维护两个表,因此我认为我需要专门拥有一个附加表建立连接。我认为这将在这些类之间创建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到许多关系-驾驶员到汽车和汽车到驾驶员。那我该如何在它们之间创建唯一的关联?

2 个答案:

答案 0 :(得分:1)

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

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

注意:更改为外键的数据注释。流畅的陈述倒转。修复了驾驶员与汽车之间处于第二种关系。

答案 1 :(得分:0)

这是创建一对零的一种简单方法。请注意,我喜欢将所有表的ID保留为Id,而不是CarId等,仅保留我的样式。这只是一个控制台应用程序,因此一旦添加EF nuget,就可以复制/粘贴。

但是下面的代码可与.net Framework 4.6和EF6.2一起使用,它将创建以下表格

汽车

  • Id(PK,int,不为null)
  • Driver_Id(FK,整数,空)

驱动程序

  • Id(PK,int,不为null)

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

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

}

}