C# SQL可能导致循环或多个级联路径

C# SQL可能导致循环或多个级联路径,c#,sql-server,C#,Sql Server,我编辑了整个问题,因为我设法找到了触发错误的原因,所以我只使用SQL方法,因为它很容易测试 可能导致循环或多个级联路径。指定删除编号 操作或更新时无操作,或修改其他外键 限制 所以问题是当我加上 CheckListGroupItemId INT FOREIGN KEY REFERENCES dbo.CheckListGroupItems(Id) ON DELETE CASCADE ON UPDATE CASCADE, 但是我需要这个来知道哪个项目属于哪个项目,但是如果我删除了ON delete

我编辑了整个问题,因为我设法找到了触发错误的原因,所以我只使用SQL方法,因为它很容易测试

可能导致循环或多个级联路径。指定删除编号 操作或更新时无操作,或修改其他外键 限制

所以问题是当我加上

CheckListGroupItemId INT FOREIGN KEY REFERENCES dbo.CheckListGroupItems(Id) ON DELETE CASCADE ON UPDATE CASCADE,
但是我需要这个来知道哪个项目属于哪个项目,但是如果我删除了ON delete CASCADE,这个功能就可以工作了

当涉及到外键和处理级联路径时,我对SQL不是很有经验-


在这种情况下,我应该怎么做?

当您删除
生产单元
行时,有两种方法可以删除
检查列表记录
。这就是它所抱怨的

ProductionUnits ->
 Cells ->
  CheckLists ->
   CheckListGroups ->
    CheckListGroupItems ->
     CheckListRecords
……还有

ProductionUnits ->
 Shifts ->
  CheckListRecords
糟糕-你不能有这个…没有真正简单的方法来解决它:-(

这是一个常见的现象。但是,有希望-有一个合理的方法来处理它…这并不可怕。当你收到这样的消息时,从它所抱怨的表中反向工作…当你在删除路径中找到指向同一个表的多个列时,你就找到了罪魁祸首

在我提出解决方案之前,有两个观察结果:首先,您已经获得了身份生成的ID。因此,在这种情况下,可能不应该指定更新级联上的
,因为ID无法随时更改(除非您在某个地方表现非常糟糕)

另一个次要的最佳实践提示:在创建表时指定模式:

CREATE TABLE dbo.ProductionUnits
(
   -- etc.
)
…并且在视图和过程中引用表时始终使用架构

如何处理冲突?您可以考虑将外键从<代码>检查记录> <代码>到代码>移位< /代码>,在“代码”>“移位< <代码> >上删除删除触发器,删除受影响的<代码>检查记录> <代码>行。很久以前…在声明引用的整数之前。rity,你必须用触发器管理关系。在这样的地方它仍然很有用

假设已将架构添加到表声明中,则大致如下:

create trigger [Shifts.Delete.Trigger] on dbo.Shifts for delete as
begin
  set nocount on;
  delete dbo.CheckListRecords where ShiftId in ( select Id from deleted );
end
看,那还不算太糟,是吗

如果出于某种原因您只需要更新
Shifts.Id
,它会变得更复杂一些。您必须在
Shifts
表上有一个备用键,以便实现逻辑…最好是一个从不更新的列,或者至少是一个在
Id
更改时不更新的列。可能是
StartTime
列?如果没有候选密钥,可以添加一个
uniqueidentifier
列。例如:

CREATE TABLE Shifts
(
  Id INT PRIMARY KEY IDENTITY,
  StartTime TIME,
  EndTime TIME,
  ShiftDescription NVARCHAR(20) ,
  ProductionUnitId INT FOREIGN KEY REFERENCES ProductionUnits(Id) ON DELETE CASCADE ON UPDATE CASCADE,
  RowId UNIQUEIDENTIFIER 
    CONSTRAINT [Shifts.RowId.Default] DEFAULT ( NEWID() )
    CONSTRAINT [Shifts.RowId.Unique] UNIQUE
)
您的大多数视图和逻辑都会完全忽略此列。您甚至不需要让EF看到它。按照我声明它的方式,它会在插入时自动设置(类似于标识列)

使用
RowId
的唯一代码是更新触发器:

create trigger [Shifts.Update.Trigger] on dbo.Shifts for update as
begin
  set nocount on;
  if update( Id ) --> just skip everything if the Id did not change
  begin
    update dbo.CheckListRecords
    set ShiftId = i.Id
    from
      inserted i
      inner join
      deleted d
      on
        i.RowId = d.RowId
      inner join
      dbo.CheckListRecords c
      on
        c.ShiftId = d.Id        
  end
end
这不是非常可怕…但并不理想。真的,如果你没有任何理由更新ID列(你真的不应该),你可以跳过所有的更新触发器和额外的列


我认为,如果你有基于触发器的引用完整性,你必须告诉EF…但EF不是我的人的方式…因此我无法帮助解决这一点。但我几乎可以肯定,当你向EF描述你的数据模型时,这是一个相当简单的声明。

我已经有一段时间没有使用EF了,但这在我和梅看来并不正确正在引起您的问题…
public virtual ICollection CheckList Records{get;set;}
public ProductionRecord ProductionRecord{get;set;}
为什么看起来不对?一个生产记录有许多检查表记录,每个检查表记录都有一个移位,您是对的……就像我说的,已经有一段时间了。:-)我会继续挖掘并让你知道。我用SQL编辑了整篇文章,这很容易通过复制粘贴进行测试,加上我发现了主要错误,但我不确定在这种情况下该怎么办,因为我总是在我看到的每个外键上设置DELETE CASCADE,所以基本上这个错误只意味着有两条路径依赖于外键,如果我从一边删除,另一边将抛出一个例外,因为这也取决于它。因为它是一个用来保存记录的表,所以我刚刚在两个外键上设置了cascade来限制EF,它就工作了。很高兴终于有人详细解释了这一点
create trigger [Shifts.Update.Trigger] on dbo.Shifts for update as
begin
  set nocount on;
  if update( Id ) --> just skip everything if the Id did not change
  begin
    update dbo.CheckListRecords
    set ShiftId = i.Id
    from
      inserted i
      inner join
      deleted d
      on
        i.RowId = d.RowId
      inner join
      dbo.CheckListRecords c
      on
        c.ShiftId = d.Id        
  end
end