简单 JSON 和 Breeze 导致错误 "call fetchMetadata first"

Simple JSON and Breeze resulting in error to "call fetchMetadata first"

我有一个 API 端点,它吐出简单的 JSON 像这样:

{
  "num_results": 2,
  "objects": [
    {
      "creator": null,
      "id": 1,
      "image": "www.test.com",
      "title": "test",
      "user_id": null
    }
}

我想使用 Breeze 将其放入 Knockout observables 中。我想我需要一个自定义 jsonResultsAdapter。我试图跟随 Edmunds 的例子,但我承认我并没有真正理解它。

var jsonResultsAdapter = new breeze.JsonResultsAdapter({
    name: "test",

    extractResults: function (data) {
        var results = data.results;
        if (!results) throw new Error("Unable to resolve 'results' property");
        return results;
    },

    visitNode: function (node, parseContext, nodeContext) {
        // Make parser
        if (node.objects) {
            return { entityType: "Pin"  }
        }
      }
    });

var dataService = new breeze.DataService( {
        serviceName: "api/",
        hasServerMetadata: false,
        jsonResultsAdapter: jsonResultsAdapter,
});    

var manager = new breeze.EntityManager( {
  dataService: dataService
});

var query = new breeze.EntityQuery.from("pin");

manager.executeQuery(query).then(function(data){
    ko.applyBindings(data);
}).fail(function(e) {
    alert(e);  
});

但这在 executeQuery 函数中失败并显示一条消息:

Error: Unable to locate a 'Type' by the name: 'Pin'. Be sure to execute a query or call fetchMetadata first.

我以为我刚刚做到了。我在这里做错了什么?

编辑

  function initialize(metadataStore) {
    var DT = breeze.DataType; // alias

    metadataStore.addEntityType({
        shortName: "Pin",
        namespace: "test",
        dataProperties: {


            creator:    { dataType: DT.String },
            id:         { dataType: DT.Int64, isPartOfKey: true },
            image:      { dataType: DT.String },
            title:      { dataType: DT.String },
            modelLinks: { dataType: DT.Int64 }
        },
    });
  }

部分理解了。需要添加一个 metadataStore 来描述返回的数据类型。但现在我得到

Error: You cannot apply bindings multiple times to the same element.

我怀疑你的 JsonResultsAdapter 没有按照你认为应该做的去做。你应该调试它看看发生了什么。

同时检查 return 发送到查询成功回调的数据。 data.results 中的对象看起来合适吗?他们有 entityAspect 属性 吗?一个entityType属性?如果没有,数据就不会成为实体,我也不认为它们会有可观察的属性。

我相信您会把问题追溯到 visitNode 方法。当你在 node.objects 上匹配时,我预测节点本身将是这样的:

{
  "num_results": 2,
  "objects": [
    {
      "creator": null,
      "id": 1,
      "image": "www.test.com",
      "title": "test",
      "user_id": null
    }, 
    ...
  ]
}

你是

的回应
return { entityType: "Pin"  }

似乎是在告诉 Breeze 这整件事 应该被视为 Pin 类型的数据。

显然不是这样。 Pin 数据在 内部 objects 数组。

您实际上想要匹配这些数据。您可以通过

这样的测试来做到这一点
if (node.id && node.title) {
    return { entityType: ...  } // see below
}

或类似的东西。

风险在于这样的测试会与 Pin 以外的某些类型的数据相匹配。如果您的服务器可以使用数据序列化类型指示符,那将会容易得多

We did this in our rails sample. We added a $type property to each entity object serialized to a Breeze client and set it to the name of the type. That made it easy to identify the node.

您必须 return 一个 EntityType 而不是类型的名称

查看 "webApi" DataServiceAdapter 的实现。

您会发现您没有return 名称 类型。你return匹配EntityType.

您需要类似这样的代码:

var type = mappingContext.entityManager.metadataStore.getEntityType('Pin', true);
return { entityType: type  }

我相信你能想象出一种更有效的方法来缓存这种类型的对象,这样你就不必在每次访问节点时都非常努力地获取它。

为什么要指定类型的命名空间

您使用 "test" 的命名空间定义了 Pin 类型。为什么?那是服务器上的名称空间吗?还是你只是编造的?如果您没有,则不需要。它只会增加不必要的复杂性。

你可能会逃脱这个:

var DT = breeze.DataType; // alias

metadataStore.addEntityType({
    shortName: "Pin",
    dataProperties: {
        creator:    { }, // string is the default
        id:         { dataType: DT.Int64, isPartOfKey: true },
        image:      { },
        title:      { },
        modelLinks: { dataType: DT.Int64 }
    },
});

更好的 extractResults 函数?

您也可以考虑改进 extractResults 方法,以便它立即摆脱多余的东西。为什么不直接进入 objects 级别呢?

extractResults: function (data) {
    var results = data.results && data.results.objects;
    if (!results) throw new Error("Unable to resolve 'results.objects' property");
    return results;
}