【问题标题】:Entity Framework object graph deletion with Breeze使用 Breeze 删除实体框架对象图
【发布时间】:2014-02-15 23:20:44
【问题描述】:

我遇到了一个反复出现但毫无意义的问题,希望有人(在 Breeze 团队中?)能解释一下。

以下模型说明了相关实体。

如您所见,我在属性名称中非常严格地遵守实体框架约定,因此,如果我在 SQL 中签入,删除规则的级联是由 EF 代码在创建数据库时首先设置的。

现在,当我尝试在 SQL 中手动删除 BusUnit 时,删除级联正确,并且相应的 BusUnitDimensions 也被删除,应该是这样。同样,如果我在 SQL 中删除了一个Dimension,相应的BusUnitDimensions 也会被删除。

但是,在我的应用程序中,如果我使用 Breeze 将 BusUnit 标记为 setDeleted,然后尝试 saveChanges,则会收到以下错误。

The operation failed: The relationship could not be changed because one
or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null
value. If the foreign-key does not support null values, a new relationship
must be defined, the foreign-key property must be assigned another
non-null value, or the unrelated object must be deleted.

但奇怪的是,如果我将 Dimension 标记为删除然后保存(在 Breeze 中),级联删除工作正常,Dimension 及其对应的 BusUnitDimensions 都被删除。

那么,为什么不一致?为什么 SQL 中的级联删除规则不适用于BusUnits,但它们却适用于Dimensions?我在其他地方读到 Breeze 不支持级联删除,但是为什么我的 Dimensions 案例有效?


编辑:

我已经删除了我之前的编辑,因为它们不相关。以下更改来自 Ward 的回答...

我的模型现在看起来像这样,BusUnitDims 现在使用BusUnitIdDimId 作为复合键,并且我添加了boolIsBud 用于有效负载。

我还没有为 BusUnits 实现删除,但是如果我尝试删除 Dim,我会收到相同的错误消息:

The operation failed: The relationship could not be changed because one
or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null
value. If the foreign-key does not support null values, a new relationship
must be defined, the foreign-key property must be assigned another
non-null value, or the unrelated object must be deleted.

我注意到级联删除不再启用,实际上,为了让 EF 构建数据库,我添加了以下配置:

modelBuilder.Entity<BusUnitDim>()
    .HasRequired(bud => bud.BusUnit)
        .WithMany(bu => bu.BusUnitDims)
        .HasForeignKey(bud => bud.BusUnitId)
        .WillCascadeOnDelete(false);

modelBuilder.Entity<BusUnitDim>()
    .HasRequired(bud => bud.Dim)
        .WithMany(d => d.BusUnitDims)
        .HasForeignKey(bud => bud.DimId)
        .WillCascadeOnDelete(false);

因此,现在明显没有级联,我可以理解为什么会发生错误。这是否意味着在控制器中,必须在删除父 Dim 或 BusUnit 时以及在调用 saveChanges 之前专门标记要删除的每个映射,或者是否有某种方法可以配置 EF 以利用级联删除,因为这将极大地简化我的控制器中的代码?

(PS:它变得更加复杂,因为BusUnitDims 最终有一个自己的连接表,MetricBusUnitDims 以容纳模型中的另一个实体及其关系。这就是我试图尽早掌握原则)


编辑:(BUSUNITS 控制器解决方案)

因此,以下方法适用于BusUnits

function deleteBusUnit(busUnitVm) { // note that you pass in the item viewmodel, not the entity
    var busUnit = busUnitVm.busUnit;
    var mapVms = busUnitVm.dimMapVms;
    var dimHash = createBusUnitDimHash(busUnit);

    mapVms.forEach(function (mapVm) {
        var map = dimHash[mapVm.dim.id];
        if (map) {
            datacontext.markDeleted(map);
        }
    });
    datacontext.markDeleted(busUnit);
    save().then(function() { getDBoardConfig(); });
    }
}

这是正确的方法吗?如果是这样,我仍然需要弄清楚以下内容:

  • 如何联系Dims。这些是不同的,因为项目视图模型是为 BusUnits 定义的。
  • 如何处理存在下一级连接表的情况,例如MetricBusUnitDIm

编辑:(DIMS 的控制器解决方案)

function deleteDim(dim) {
    return bsDialog.deleteDialog(dim.name, true)
        .then(function () {
            vm.busUnitVms.forEach(function (busUnitVm) {
                busUnitVm.busUnit.busUnitDims.forEach(function (bud) {
                    if (bud.dimId === dim.id) {
                        datacontext.markDeleted(bud);
                    }
                });
            });
            datacontext.markDeleted(dim);
            save().then(function () { getDboardConfig(); });
        });
}

【问题讨论】:

  • 通常避免专门向用户寻求帮助 - 只是考虑负载,但如果您正在寻求个人的特定支持,您是否考虑过付费支持?
  • @PWKad 谢谢。对 SO 来说相对较新,如果我违反了不成文的礼仪,请道歉。我目前正在与有关公司进行讨论,但我们的时差使势头变慢。

标签: entity-framework breeze cascading-deletes


【解决方案1】:

我相信您的问题可以追溯到这样一个事实,即您的映射表 BusUnitDimension 有自己的主键 Id,而不是更典型的方法,其中 BusUnitIdDimensionId FK 属性共同组成BusUnitDimension的复合主键。

注意 Northwind 中的 OrderDetails 和 Breeze 多对多示例中的 HeroPoweMap 具有复合键。

您的选择会带来麻烦。

首先,可以创建多个 BusUnitDimension 实体来表示 BusUnitDimension 之间的相同关联(即它们都具有相同的 FK 对)。数据库可能能够防止这种情况发生(我已经很久没有看过了),但无论是否这样做,它都不会阻止您在 Breeze 中创建这些重复项......也许在 EF 中也不会。

其次,它让您了解当前面临的问题。如果这些映射实体在您执行删除时位于 DbContext 中,则 EF 可能(显然会)尝试将其 FK 属性设置为空,因为它将 BusUnitDimension 设置为已删除状态。

正如建议的那样,您可以通过将BusUnitIdDimensionId FK 属性都设为空来解决此问题。但这与语义相反,因为 BusUnitDimension 必须将真实的 BusUnit 链接到真实的 Dimension;它们不是可选的。实际后果可能是,如果您这样做,您不会从 EF 角度获得级联删除(不确定数据库是否会强制执行)。这意味着您将在数据库中孤立BusUnitDimension 行,其中一个或两个FK 为空。我推测是因为我不习惯陷入这种麻烦。

另一种方法是将它们的 FK 值设置为零(我认为 Breeze 会为您做到这一点)。当然,这意味着BusUnitDimension 表行与Id == 0 的存在,如果仅在删除操作期间。

顺便说一句,您的数据库中实际上可以有这样的“哨兵实体”。

您必须确保这些 BusUnitDimension 处于已删除状态,否则 EF(和 DB)将拒绝它们(参照完整性约束)或孤立它们(您的数据库中将有 BusUnitDimension 行与一个或两个 FK 都为零)。

或者,如果您知道数据库将级联删除它们,您可以简单地将它们从DbContext 中删除(从EFContextProvider 中的EntityInfoMap 中删除)。但现在你必须告诉 Breeze 客户端,如果碰巧让它们闲逛,你也必须摆脱它们。

已经够了!

这些飘忽不定的想法应该告诉你,你已经把自己搞得一团糟,记账太多了……这都是因为你给了BusUnitDimension 自己的Id 主键。

如果给BusUnitDimension 提供复合键{BusUnitId, DimensionId},它会变得容易得多。您还必须给它一个有效负载属性(任何事情都可以),以防止 EF 将其隐藏在其“多对多”实现中,因为 Breeze 不处理它。添加任何无意义的属性都可以解决问题。

HTH

【讨论】:

  • @AdelSal Ward,一如既往地感谢您的全面回复。由于这个问题,我正忙于对我的代码进行一些内务处理,这最终成为一件好事,因为我在此过程中清理了许多其他小问题。我一定会实施您的建议,我会发布我遇到的任何问题,以供可能感兴趣的人使用。一旦我解决了这个问题,我想我会为这个主题的博客提供一些不错的内容:-)
  • 好的,我已经按照我的理解执行了这个建议,但是在尝试删除父实体时我仍然收到了那个可怕的消息,所以很明显我仍然做错了什么。我将发布一些额外的内容作为编辑。
  • 我现在在多对多工作的两边都有删除,对于那些感兴趣的人,我已经为第二面添加了一个编辑,因为如果你不想要 EF 错误,它会略有不同.不过,我必须说(在这次经历之后),微风缺乏对 EF 多对多关系的本地支持会带来很多复杂性。例如,在我的情况下,如果我想删除 dboardConfig,我将需要添加首先处理 busUnits and dims 的控制器逻辑。如果我添加更多实体,我需要再次访问。恕我直言,这是需要通过微风来处理的事情。
【解决方案2】:

这与 Breeze 无关。原始消息来自实体框架。 内部BusUnitDimension 模型更新BusUnitId 属性为:

 public Nullable<int> BusUnitId { get; set; }

注意 Nullable 结构..

【讨论】:

  • 嗨。这也是我的第一直觉,但我不明白为什么在删除父实体时,级联删除适用于 1-m-1 的一侧,而不适用于另一侧。无论如何,不​​幸的是,您提出的解决方案不起作用,因为现在,当删除 BusUnit 时,BusUnitDimensions 表中的 BusUnitId 将被清空。我正在寻找的结果是BusUnitDimensions 中的每个条目,其中BusUnits 表中的BusUnit.Id 中的每个条目都应该在删除选定的BusUnit 时被删除。
  • 如果您在数据库中启用了级联删除。这应该够了吧。我以前就参与过这个,这对我有用。如果您想解决问题,请考虑在您的问题中添加更多详细信息(例如模型类、客户端删除命令)。
  • 顺便说一句,如果您将我的回答应用于您之前的问题。在客户端删除 BusUnit 也应该清除它的子集合
  • 我将添加更多信息作为附加编辑。不幸的是,您之前的建议没有删除孩子。如果您有兴趣深入研究这个问题,请给我发电子邮件(请参阅我的个人资料),也许我们可以在线进行一次快速的结对编程会议。我总是热衷于与他人合作并分享我的方法。
  • 当然,但是 SO 中的电子邮件设置为私密。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 2018-05-19
  • 1970-01-01
相关资源
最近更新 更多