【问题标题】:Check if it is safe to delete a row检查删除一行是否安全
【发布时间】:2010-11-23 14:04:19
【问题描述】:

我希望能够检查在 SQL Server 2008 中从表中删除一行是否会因为外键违规而失败,而不尝试删除它。

基本上,如果用户无法删除它,我不想向用户显示删除按钮,因为该密钥在其他地方使用。

我在应用程序的很多地方都需要这个,所以我真的不想手动编写检查来查看删除行是否安全。对实现这一目标的最佳方式有何建议?

我正在使用实体框架来访问数据。

【问题讨论】:

    标签: c# sql-server entity-framework sql-server-2008 asp.net-mvc-2


    【解决方案1】:

    没有快速简便的方法来检查这一点。您可能可以使用information_schema 构建动态的东西,但它无疑会很丑而且速度不是很快。

    绝对的最佳选择是验证每个位置所需的几行自定义代码。

    另一种选择是启动事务,尝试删除。如果它失败了,那么你就知道了。如果成功,回滚事务,你知道删除是可能的。这仍然很丑陋,并且以某种破碎的方式使用事务,但它会起作用。不过,请确保该表未启用级联删除。

    【讨论】:

      【解决方案2】:

      查询时,对子表执行 LEFT JOIN。使用 CanDelete 计算值来决定是否应显示按钮。如果每个父行有 1 个以上的子行,此处的 COUNT 将删除重复项。

      SELECT
         Col1, Col2, Col3, ...,
         CASE C.Existence WHEN 0 THEN 1 ELSE 0 END AS CanDelete
      FROM
         ParentTable P
         LEFT JOIN
         (
         SELECT COUNT(*) AS Existence, FKColumn
         FROM Childtable GROUP BY FKColumn
         ) C ON P.FKColumn = C.FKColumn
      WHERE
         P.Col = ...
      

      另一种可能是

      SIGN(C.Existence) AS HasChildRows
      

      【讨论】:

      • EF 没有办法做到这一点吗? (也许会看到一个视图?)
      • @veljkoz,@Jeff Sternal:抱歉,不知道。您可以将其包装在上述视图中。
      【解决方案3】:

      我在之前的申请中做过这种事情。我创建了一个名为 TryDelete() 的函数。在方法内部,我试图删除所需的行。如果我得到一个 FK 异常,我会抓住它并返回 false。无论是真还是假,我都将删除封装在事务中,然后将其回滚。

      【讨论】:

        【解决方案4】:

        您可以在实体的部分类中添加一个方法来检查引用的对象是否存在。

        例如,假设您有 Entity1,其中包含 Entity2 的集合。基本上,在每个实体部分类中,您都会编写一个属性IsReferenced,它会:

        • 对于 Entity1,如果 Entity1 在 Entity2 中有任何项目,则返回 true
        • 对于 Entity2,如果有对 Entity1 的引用,则返回 ture

        正如您所猜测的,您需要确保始终在 fetch 中包含引用的值,或者,如果您正在附加上下文,您可以在 IsReferenced 中使用 .Load() 来获取实体检查之前。这是一项开销,这取决于您是否愿意为此“付费”。

        然后,您可以根据需要根据该元素属性显示/隐藏“删除”按钮,从而避免每次都重复检查。

        【讨论】:

          【解决方案5】:

          我认为您在这里有 2 个可能的选择。由于您无法保证所有关系都将映射到您的 OM 中,因此您必须在数据库中进行检查。

          您可以尝试在事后回滚的事务中进行实际删除,但如果您必须使用级联删除配置约束,这也可以工作......

          另一种方法是从 sysobjects 表中提取所有约束,并验证每个表都没有记录。但这需要一些动态 SQL,这也会变得非常混乱。

          【讨论】:

            【解决方案6】:

            如果您处于数据库级别,我将加入所有可能存在冲突的表。

            任何返回的记录都不能被删除,也就是说剩下的记录可以被删除。

            【讨论】:

              【解决方案7】:

              假设该数据库由多个用户(绝大多数用户)使用 - 在“检查”删除是可能的和用户可能决定删除该行之间会有一个机会窗口,在此期间有人else 可能会执行一些否定测试结果的活动。

              这意味着您可能会显示“删除”按钮,但当您尝试删除时,它已不再可能。此外,您可能不会显示“删除”按钮,但当用户决定要删除该行(但找不到该按钮)时,应该允许他们这样做。

              没有办法避免这类比赛。如果他们愿意,我只会让人们尝试删除,但要准备好处理由于外键导致的失败。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2022-08-18
                • 1970-01-01
                • 2010-10-07
                • 1970-01-01
                • 2017-11-05
                • 2010-10-30
                • 1970-01-01
                相关资源
                最近更新 更多