3

I'm struggling to debug this error because, although it's consistently reported, the app's behavior is as intended. Would appreciate pointers as to what it means and how I could go about debugging its source.

Apologies for being vague but, since I'm getting the desired result, I'm unsure what other information to provide.

UPDATE

I've created a repro of this issue and attempted to focus the code on the problem. The error is thrown consistently even though the database is updated correctly. There's a single saveChanges in the code and it uses the save functionality from the dataservice.js in the Breeze Todo sample. SaveOptions.allowConcurrentSaves is false.

Entirely at a loss to explain it and have looked through my EF code to see whether I'm making an obvious mistake but can't see it. The bundle sent to the WebAPI SaveChanges method looks correct (correctly populated with IDs etc.) too.

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

UPDATE 6th February

The issue remains unresolved by Wade's helpful answer. Unfortunately, unless I can understand what it is I'm doing wrong or learn that this is a bug, I'm going to have to abandon the use of Breeze in this project and revert to crappy, plain old AJAX calls.

It would appear that the issue revolves around the server returning a zeroed GUID when saving changes. The method returns no errors. I would be thrilled to learn that this is a bug in my entity model but I'm doubtful.

Here's the failure:

breeze.debug.js: 11954

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

When the code reaches this point, the value of this._indexMap is correct and is:

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

BUT the value of tempValue (as returned from the Web API call by the server) is wrong:

00000000-0000-0000-0000-000000000000

The realValue is correct and is:

1093b975-7686-4621-8336-77c38ed36de0

Backing up the stack. Here are the results from the AJAX call, breeze.debug.js: 12574. See that the tempValue is zeroed on return from the server/WebAPI call. The realValue is correct. This is what the database contains. The row is added to the table without problem.

"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"
    }

],

Verified against what's received serverside in saveBundle. NB the IDs of both entities received at the server have valid GUID IDs.

"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"
            }
        }
    }
],

Unsurprisingly, the values sent to the server by the AJAX call that are created in breeze.debug.js: 10494 saveBundleStringified are correct and the same as those received by the server (won't reproduce but I assure you they are).

And, from my code, when the saveChanges is called,

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

and, as expected, these match the (temp) values of the entities' IDs during saveChanges, received by the server...

What am I doing wrong?? If I had hair, I'd be tearing it out!

4

4 回答 4

5

我已经解决了。

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

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

所以:

public Guid ID { get; internal set; }

应该:

public Guid ID { get; set; }
于 2013-02-06T22:48:57.820 回答
3

1月27日更新:

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

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

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

返回 manager.saveChanges()
              .then(保存成功)
              .失败(保存失败);

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

数据服务.saveChanges()
           .then(万岁)
           . 失败(悲伤长号);

并发保存

我在您的代码中注意到您正在使用您在Todo示例中找到的时间延迟方法来防止非法并发保存。

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

如果您需要非阻塞保存,请查看“Cool Breezes”部分下的“ Concurrent Saves ”主题中描述的“ Save Queuing ”插件。

简洁地创建实体

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

导出函数 Business(manager, o: IBusiness) {
  var business = manager.metadataStore.getEntityType("Business").createEntity();
  业务.ID(breeze.core.getUuid());
  业务名称(o.name);
  manager.addEntity(business);
  退货业务;
}

可以很简单:

导出函数 Business(manager, o: IBusiness) {
  返回 manager.createEntity("业务", {
    ID:微风.core.getUuid(),
    姓名:o.name,
  });
}

快捷方式是新的EntityManager.createEntity,因为您编写了这段代码,所以不要因为错过它而感到难过。

原答案:

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

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

于 2013-01-04T22:20:34.480 回答
1

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

希望这可以帮助。

于 2013-01-04T22:15:01.203 回答
1

我有同样的问题,我已经在 jQuery 延迟对象的帮助下解决了这个问题。我的数据服务保存方法现在如下所示:

saveChanges = function () {
    var def = $.Deferred();

    if (manager.hasChanges()) {
        manager.saveChanges()
            .then(function () { def.resolve() })
            .fail(function (error) {
                handleSaveError(error);
                def.reject();
            });
    } else {
        logger.info("Nothing to save");
        def.resolve();
    };

    return def.promise();
};

我也使用 jQuery 延迟对象从我的视图模型中调用它:

$.when(dataservice.saveChanges())
 .done(function () {
      ...some actions
 })
于 2013-01-21T21:19:23.833 回答