ASP.NET EF核心:由两个未正确评估的外键组成的复合键

时间:2020-02-21 14:05:45

标签: c# sql asp.net-core entity-framework-core

编辑,经过一些编辑后,我现在可以发布数据,但是在搭建结构时遇到了问题。我的模型构建者现在感觉好多了,但仍然有问题

modelBuilder.Entity<Client>().HasMany(x => x.Machines).WithOne(x=>x.Client).HasForeignKey(x=>x.ClientID);
modelBuilder.Entity<Machine>().HasKey(x => new { x.ID, x.ClientID });
modelBuilder.Entity<MachineBob>().HasOne(x => x.Machine).WithMany(x => x.MachineBobData).HasForeignKey(x => new { x.MachineID, x.ClientID }).OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<MachineMixer>().HasOne(x => x.Machine).WithMany(x => x.MachineMixerData).HasForeignKey(x => new { x.MachineID, x.ClientID }).OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<MachineBob>().HasKey(c => new { c.TimeStamp, c.ClientID, c.MachineID });
modelBuilder.Entity<MachineMixer>().HasKey(x => new { x.TimeStamp, x.MachineID, x.ClientID });

END-编辑

我正在尝试使用EF Core构建一个Web应用程序,该应用程序将允许客户端拥有任意数量的计算机并记录其数据。每台机器可以具有不同的类型,并且每种机器在记录不同内容时都有唯一的表。我能够轻松创建Client and Machine对象,但是当我尝试将数据记录到Machinexxx数据表中时,便遇到了有关使用复合外键的问题。

CloudApp.Models

public class Client 
{
    [Required]
    [MaxLength(100)]
    public string ClientName { get; set; }
    [MaxLength(100)]
    [Key]
    public int ID { get; set; }
    [MaxLength(100)]
    // TODO: Automatically assign these email addresses as CLAIMS to allow the USER to see the CLIENT
    public List<String> Emails;
    public ICollection<Machine>? Machines { get; set; }
}

这里的Machine类与一对多关系

public class Machine
{
    [MaxLength(50)]
    [Key]
    public int ID { get; set; }

    [MaxLength(100)]
    public string Factory { get; set; }
    [MaxLength(50)]
    public string Line { get; set; }

    [MaxLength(50)]
    public string MachineType { get; set; }
    [MaxLength(50)]
    public string MachineName { get; set; }
    [MaxLength(50)]
    public string Country { get; set; }
    [MaxLength(50)]
    public string City { get; set; }
    [MaxLength(25)]
    public int ZipCode { get; set; }
    [MaxLength(50)]
    public string Address { get; set; }

    [MaxLength(50)]
    public int ClientID { get; set; }
    public Client Client { get; set; } // Navivation property
    public List<MachineBob>? MachineBobData { get; set; } // Navivation property
    public List<MachineMixer>? MachineMixerData { get; set; }// Navivation property

与(当前)两种可能的机器类型有一对多的关系

public class MachineBob 
{
    [MaxLength(50)]
    [XmlElement("TimeStamp")]
    public DateTime TimeStamp { get; set; }
    [XmlElement("Temperature")]
    [MaxLength(50)]
    public int Temperature { get; set; }
    [MaxLength(50)]
    [XmlElement("Heartbeat")]
    public Boolean Heartbeat { get; set; }

    [MaxLength(50)]
    [ForeignKey("MachineID")]
    public int MachineID { get; set; }
    public Machine Machine { get; set; }
    [MaxLength(50)]
    [ForeignKey("ClientID")]
    public int ClientID { get; set; }
    public Client Client { get; set; }
}

[XmlRoot("Root"),Serializable]
public class MachineMixer 
{
    [MaxLength(50)]
    [XmlElement("TimeStamp")]
    public DateTime TimeStamp { get; set; }
    [XmlElement("Temperature")]
    [MaxLength(50)]
    public int MixingRatio { get; set; }
    [MaxLength(50)]
    [XmlElement("Heartbeat")]
    public Boolean Heartbeat { get; set; }
    [XmlIgnore]

    [MaxLength(50)]
    public int MachineID { get; set; }
    public Machine Machine { get; set; }
    [MaxLength(50)]
    public int ClientID { get; set; }
    public Client Client { get; set; }
}

最后,我的模型构建器是这样的(我的所有关系都是在这里建立的,由于它们的限制,我尝试不使用数据注释)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
        base.OnModelCreating(modelBuilder);

        foreach (var foreignKey in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
        {
            foreignKey.DeleteBehavior = DeleteBehavior.Cascade;
        }

        modelBuilder.Entity<Client>().HasMany(x => x.Machines);

        modelBuilder.Entity<Machine>().HasOne(x => x.Client).WithMany(x => x.Machines).HasForeignKey(x => x.ClientID);
        modelBuilder.Entity<Machine>().HasKey(x => new { x.ClientID, x.ID });

        modelBuilder.Entity<Machine>().HasMany(x => x.MachineBobData).WithOne(x => x.Machine).HasForeignKey(x => new { x.MachineID, x.ClientID })OnDelete(DeleteBehavior.NoAction); 
        modelBuilder.Entity<Machine>().HasMany(x => x.MachineMixerData).WithOne(x => x.Machine).HasForeignKey(x => new { x.MachineID, x.ClientID }).OnDelete(DeleteBehavior.NoAction);

        modelBuilder.Entity<MachineBob>().HasKey(c => new { c.TimeStamp, c.ClientID, c.MachineID });

        modelBuilder.Entity<MachineMixer>().HasKey(x => new { x.TimeStamp, x.MachineID, x.ClientID });
}

我想做的是让每一行都是一个数据条目,并带有一个由MachineID, ClientID, Timestamp组成的唯一复合键。但是正如我在下面解释的那样,目前尚不可能...

正如我之前提到的,对于bot的所有CRUD操作,Client和Machine类都可以正常工作。但是MachineBob的问题令人难以置信。即使模型设计看到存在三个元素的组合键,但每次尝试写入表时,都会收到SQL错误。十分奇怪的是,只要我使用ClientID = 1,MachineID = 1和唯一的时间戳,就可以按预期进行编写。但是,一旦我尝试通过写入Client ID = X来添加此计算机BOB的另一行或另一个实例,就会开始出现问题。

这是SQL异常,似乎只有ClientId不是唯一的问题。但这是组合键的一部分,因此无论如何都不会出现此问题:

SqlException:INSERT语句与FOREIGN KEY约束“ FK_MachineBobs_Clients_ClientID”冲突。在数据库“ aspnet-ProcessCloudApp-B3DBF3A9-F26C-41F4-B7D6-CC2D2782C266”的表“ dbo.Clients”的“ ID”列中发生了冲突。该声明已终止。

我想为我的建模提供一些帮助,因为我对此最感到困惑。

我的一部分感觉到问题还可能在于我对这种“ MachineBob”数据模型的思考方式。取而代之的是,在“ Machine”和“ MachineBob”之间具有一对一的关系,并具有类型ICollection<T>List<T>

的所有属性可能会更好。

谢谢您的帮助。

1 个答案:

答案 0 :(得分:0)

我能够解决此问题(仅花了整整一周的时间)。我相应地修改了我的模型构建器,但也删除了MachineBOB中的“公共客户端客户端”。即使我需要ClientID,对象也不是直接相互连接的。这是代码,供任何发现它值得进一步批评或可供自己在这种半复杂关系中使用的人参考。

        modelBuilder.Entity<Client>().HasMany(x => x.Machines).WithOne(x=>x.Client).HasForeignKey(x=>x.ClientID);
        modelBuilder.Entity<Client>().HasKey(x => x.ID);
        modelBuilder.Entity<Machine>().HasKey(x => new { x.ID, x.ClientID });
        modelBuilder.Entity<MachineBob>().HasOne(x => x.Machine).WithMany(x => x.MachineBobData).HasForeignKey(x => new { x.MachineID, x.ClientID}).OnDelete(DeleteBehavior.NoAction);
        modelBuilder.Entity<MachineMixer>().HasOne(x => x.Machine).WithMany(x => x.MachineMixerData).HasForeignKey(x => new { x.MachineID, x.ClientID }).OnDelete(DeleteBehavior.NoAction);
        modelBuilder.Entity<MachineBob>().HasKey(c => new { c.TimeStamp, c.ClientID, c.MachineID });
        modelBuilder.Entity<MachineMixer>().HasKey(x => new { x.TimeStamp, x.MachineID, x.ClientID });

和班级

[XmlRoot("Root"),Serializable]
public class MachineBob 
{
    [MaxLength(50)]
    [XmlElement("TimeStamp")]
    public DateTime TimeStamp { get; set; }
    [XmlElement("Temperature")]
    [MaxLength(50)]
    public int Temperature { get; set; }
    [MaxLength(50)]
    [XmlElement("Heartbeat")]
    public Boolean Heartbeat { get; set; }

    [MaxLength(50)]
    [ForeignKey("MachineID")]
    public int MachineID { get; set; }
    public Machine Machine { get; set; }
    [MaxLength(50)]
    [ForeignKey("ClientID")]
    public int ClientID { get; set; }