【问题标题】: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 现在使用BusUnitId 和DimId 作为复合键,并且我添加了bool、IsBud 用于有效负载。

我还没有为 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,而不是更典型的方法,其中 BusUnitId 和 DimensionId FK 属性共同组成BusUnitDimension的复合主键。

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

您的选择会带来麻烦。

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

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

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

另一种方法是将它们的 FK 值设置为零(我认为 Breeze 会为您做到这一点)。当然,这意味着BusUnit 和Dimension 表行与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
相关资源
最近更新 更多