“密钥修复中的内部错误 - 无法定位实体”错误,但数据库按预期更新

时间:2013-01-04 21:06:41

标签: breeze

我正在努力调试此错误,因为虽然它一直报告,但应用程序的行为与预期一致。希望指出它的意义以及我如何调试其来源。

道歉,但是,由于我得到了理想的结果,我不确定要提供的其他信息。

更新

我已经创建了这个问题的副本,并试图将代码集中在问题上。即使正确更新数据库,也会一致地抛出错误。代码中有一个saveChanges,它使用Breeze Todo示例中dataservice.js的保存功能。 SaveOptions.allowConcurrentSaves为false。

完全无法解释它,并查看了我的EF代码,看看我是否犯了一个明显的错误,但看不到它。发送到WebAPI SaveChanges方法的包看起来也正确(正确填充了ID等)。

https://github.com/DazWilkin/BreezeJS.ScoreIssue

2月6日更新

韦德的有用答案仍然没有解决这个问题。不幸的是,除非我能理解它是错误的或者知道这是一个错误,否则我将不得不放弃在这个项目中使用Breeze并恢复到糟糕的,简单的旧AJAX调用。

似乎问题围绕服务器在保存更改时返回归零的GUID。该方法不返回任何错误。我很高兴得知这是我实体模型中的一个错误,但我很怀疑。

这是失败:

breeze.debug.js:11954

var ix = this._indexMap[tempValue];
if (ix === undefined) {
   throw new Error("Internal Error in key fixup - unable to locate entity");
}

当代码到达这一点时,this._indexMap的值是正确的,并且是:

{"bcb6e670-00fc-469d-8531-5767f40bf3c1":0}

但是tempValue的值(由服务器的Web API调用返回)是错误的:

00000000-0000-0000-0000-000000000000

realValue是正确的,是:

1093b975-7686-4621-8336-77c38ed36de0

备份堆栈。以下是来自AJAX调用的结果,breeze.debug.js:12574。看到tempValue在从服务器/ WebAPI调用返回时归零。 realValue是正确的。这是数据库包含的内容。该行没有问题地添加到表中。

"KeyMappings": [
    {
        "$id": "4",
        "$type": "Breeze.WebApi.KeyMapping, Breeze.WebApi",
        "EntityTypeName": "...Score",
        "TempValue": "51877f5b-811f-4260-bd5b-cf9965159597",
        "RealValue": "92b73b8a-8b33-45cd-9822-ca7c0c5d5d9a"
    },
    {
        "$id": "5",
        "$type": "Breeze.WebApi.KeyMapping, Breeze.WebApi",
        "EntityTypeName": "...PropertyValue",
        "TempValue": "00000000-0000-0000-0000-000000000000",
        "RealValue": "1093b975-7686-4621-8336-77c38ed36de0"
    }

],

验证saveBundle中收到的服务器端。注意,在服务器上收到的两个实体的ID都有有效的GUID ID。

"entities": [
    {
        "ID": "51877f5b-811f-4260-bd5b-cf9965159597",
        ...
        "entityAspect": {
            "entityTypeName": "Score:...",
            "entityState": "Added",
            "originalValuesMap": {},
            "autoGeneratedKey": {
                "propertyName": "ID",
                "autoGeneratedKeyType": "Identity"
            }
        }
    },
    {
        "ID": "bcb6e670-00fc-469d-8531-5767f40bf3c1",
        ...
        "entityAspect": {
            "entityTypeName": "PropertyValue:...",
            "entityState": "Added",
            "originalValuesMap": {},
            "autoGeneratedKey": {
                "propertyName": "ID",
                "autoGeneratedKeyType": "Identity"
            }
        }
    }
],

不出所料,在breeze.debug.js:10494 saveBundleStringified中创建的AJAX调用发送给服务器的值是正确的,与服务器接收的值相同(不会重现,但我向你保证)

而且,从我的代码中,当调用saveChanges时,

manager.getChanges().length == 2
manager.getChanges()[0].ID() == "51877f5b-811f-4260-bd5b-cf9965159597" (Score)
manager.getChanges()[1].ID() == "bcb6e670-00fc-469d-8531-5767f40bf3c1" (PropertyValue)

并且,正如预期的那样,这些匹配在服务器接收的saveChanges期间实体ID的(temp)值...

我做错了什么?如果我有头发,我会把它撕掉!

3 个答案:

答案 0 :(得分:5)

我已经解决了。

我的不一致应用(!)约定是在代码优先类型上使setter内部/私有。我说不一致,因为在觉得我已经用尽所有可能性之后,我发现PropertyValue类型,即发起错误的类型,有一个内部集。

删除了这个并重建了解决方案后,问题就解决了!

所以:

public Guid ID { get; internal set; }

应该是:

public Guid ID { get; set; }

答案 1 :(得分:3)

1月27日更新:

根据您对谢尔盖答案的评论,您可能在保存操作完成之前尝试对已更改的实体执行某些操作。

这些实体保持更改状态...通常使用临时主键和外键...直到服务器报告成功保存。

在保存成功之前,您可能不应该触摸它们。正如Sergey所说,您应该在保存成功回调中找到保存后的处理。

return manager.saveChanges()
              .then(saveSucceeded)
              .fail(saveFailed);

您不应将saveChanges调用包装在jQuery Deferred 中。这是浪费时间和复杂性。 EntityManager.saveChanges方法返回调用者可以使用的承诺。视图模型可以添加自己的成功和失败回调

dataservice.saveChanges()
           .then(hooray)
           .fail(sadTrombone);

并发保存

我在您的代码中注意到您使用 Todo 示例中的time-delay approach来防范非法并发保存。

这种方法实际上只适用于演示。如果您的视图模型需要在保存成功时执行某些任务,那么它将无法正常工作。它不起作用,因为dataservice无法使用时间延迟方法向视图模型返回承诺。

如果您需要非屏蔽保存,请查看“酷风”部分下“Save Queuing”主题中所述的“Concurrent Saves”插件。

简洁地创建实体

在查看代码时,我无法注意到 scoreissue.1.0.ts 中的实体工厂方法有点冗长。你写的是:

export function Business(manager, o: IBusiness) {
  var business = manager.metadataStore.getEntityType("Business").createEntity();
  business.ID(breeze.core.getUuid());
  business.Name(o.name);
  manager.addEntity(business);
  return business;
}

可能很简单:

export function Business(manager, o: IBusiness) {
  return manager.createEntity("Business", {
    ID: breeze.core.getUuid(),
    Name: o.name,
  });
}

EntityManager.createEntity快捷方式是新的,因为您编写了此代码,所以不要错过它。

原始答案:

[错误的方向。保留理解DazWilkin评论问题是在客户端。]

这是在哪里生成的?在服务器上?如果是这样,您可以继承EFContextProvider并覆盖SaveChangesCore。调用base.SaveChangesCore并在其周围放置一个try / catch。检查saveMap参数。 EFContextProvider是开源的;我开始在这里挖掘。

答案 2 :(得分:1)

当涉及密钥生成的多个同时保存请求同时挂起时,可能会发生此错误。此错误仅在保存期间发生吗?如果是这样,请尝试将SaveOptions.allowConcurrentSaves设置为false。如果这导致出现不同的错误(并发保存错误),那么您的问题肯定与并发保存有关。

希望这有帮助。