为什么我的Breeze.js实体没有创建ko.observables?

PlT*_*lor 4 knockout.js breeze

我在没有服务器端组件的情况下使用Breeze.js,并使用以下代码在客户端创建实体.按照Ward的要求,我简化了一切,并提供了更多信息.我的MetaDataStore配置功能 -

function configureMetadataStore(metadataStore) {
        metadataStore.addEntityType({
            shortName: 'Manufacturer',
            namespace: 'StackAndReach',
            autoGeneratedKeyType: breeze.AutoGeneratedKeyType.Identity,
            dataProperties: {
                id: { dataType: DT.Int64, isPartOfKey: true },
                name: { dataType: DT.String },
                website: { dataType: DT.String},
                approved: {dataType: DT.boolean},
                user_id: { dataType: DT.Int64 }
            }
        });
    }
Run Code Online (Sandbox Code Playgroud)

我的服务器的JSON响应

{"id":"141","name":"Trek","website":"http:\/\/www.trekbikes.com\/","approved":"1","user_id":"3"}
Run Code Online (Sandbox Code Playgroud)

来自我的datacontext的配置代码(整个设置减去服务器元数据的缺失是在John Papa的课程之后设置的)

var entityQuery = breeze.EntityQuery,
        manager = configureBreezeManager();
function configureBreezeManager() {
        breeze.NamingConvention.camelCase.setAsDefault();
        var ds = new breeze.DataService({
            serviceName: config.remoteServiceName,
            hasServerMetadata: false
        });

        var mgr = new breeze.EntityManager({dataService : ds});
        model.configureMetadataStore(mgr.metadataStore);
        return mgr;
    }
Run Code Online (Sandbox Code Playgroud)

当模型被拉下来时,数据就在那里,但是数据没有包含在ko.observables中,并且init函数中的ko.observables/ko.computed不在传递出查询的模型中.如何确保模型的数据包含在ko.observables中并添加了ko.computeds?

War*_*ard 11

这个答案同样是一个分析问题的教程,作为问题的答案.

第1步 - 简化和隔离

让我们彻底简化,直到找到缺失的步骤.让我们从你拥有的最简单的实体类型开始......最多有2到5个属性.没有吗?做一个.减少Manufacturer到只是"id"和"名称".我们试图首先让机制失效.

您没有在服务器上使用任何breeze组件.精细.选择提供该测试实体数据的服务器端点.让该端点只提供一个实例的JSON数组.向我们展示到达客户端的JSON ...整个JSON有效负载,就像它到达线路一样.应该简短; 如果不是,你还没有简化.

那么我们可以弄清楚你是否需要一个JsonResultsAdapter以及它应该是什么样子.

向我们展示metadataStore使用EntityType,ctor和初始化程序填充的确切顺序.坦率地说,在我们让第一个工作之前,我宁愿没有ctor或初始化器.

你如何确保EntityManager你创建的是使用该商店?我们需要查看您的配置代码以及如何新建EntityManager并使用它来查询端点.

如果你按照我的建议,将没有太多的代码.也许二十行.JSON提要大约应该是10行.如果你不能打这些数字,你还没有简化.

第2步 - 查看更简单的示例

既然你重新开始了这个例子,我就会更好地了解在哪里看.

有两件事情在我身上飞跃:

  1. 来自服务器的JSON结果
  2. camelCase命名约定

来自服务器的JSON

让我们打印您提供的JSON结果并讨论它们:

{
  "id": "141",
  "name": "Trek",
  "website": "http:\/\/www.trekbikes.com\/",
  "approved": "1",
  "user_id": "3"
}

Breeze不知道如何处理该JSON对象,因为它缺少类型信息.微风它只是一个任意对象,也许是投影的结果.

将其与查询DocCode Web API的查询的JSON结果进行比较.这是为查询生成的URL:

>http://localhost:47595/breeze/northwind/Suppliers/?$top=1

这是(缩写)JSON结果

[
   {
      "$id":"1",
      "$type":"Northwind.Models.Supplier, DocCode.Models",
      "SupplierID":1,
      "CompanyName":"Exotic Liquids"
   }
]

默认情况下,Breeze客户端需要使用JSON.NET(ASP.NET中的默认序列化程序)序列化的数据.

JSON.NET有效负载是节点或节点数组.JSON.NET增加了自己$id$type性能到每个节点.

我想将注意力集中在$type您可能认为是.NET类型的全名(class-with-namespace,assembly-name)的属性上.

你可以在没有$id财产的情况下离开.

$ id是一个自动递增的序列化密钥.通常,同一对象在有效载荷中出现多次.JSON.NET不是重复内容,而是替换一个简单的节点,就像 {$ref: #}在哪里#引用$id早期节点一样.这种方法既减少了有效负载大小又打破了循环引用.

但是Breeze真的很期待那个$type房产.这就是它将JSON对象/节点连接到元数据中的类型的方式.如果您的制造商示例节点有一个,它可能是这样的:

"$type": "StackAndReach.Manufacturer, MyModel"

我不知道你是如何在服务器上序列化数据的.看起来你使用的不是JSON.NET.

太棒了.我只是告诉你Breeze默认是如何工作的; 它非常友好.但是Breeze不需要.NET.它是一个纯JavaScript库.你只需告诉它你想要什么.

使用toType(...)

您可以做的最简单的事情是添加toType到您的查询中.

var query = breeze.EntityQuery.from('Manufacturers')
                  .where( ... )
                  .toType( 'Manufacturer' );

通过这种方式,您明确声明"制造商"端点返回的顶级节点包含Manufacturer您在元数据中描述的类型的数据.

我敢打赌这对你有用(一旦你解决了下面描述的命名约定问题).

这是一种有效的方法,但它有几个缺点.我会提到两个:

  1. 您必须记住将其添加到每个查询中.

  2. 它仅适用于顶级实体; if不适用于嵌套实体,例如在应用.expand()子句时返回的实体.

我更喜欢教Breeze客户端如何自己解释JSON结果......使用自定义JsonResultsAdapter.

自定义JsonResultsAdapter

查看Breeze Edmunds示例,其中Breeze客户端使用Edmunds车辆信息服务中的数据.

Edmunds服务器发送完全不同类型的JSON有效负载以响应查询.这是一个片段:

{
   "makeHolder":[
      {
         "id":200347864,
         "models":[
            {
               "link":"/api/vehicle/am-general/hummer",
               "id":"AM_General_Hummer",
               "name":"Hummer"
            }
         ],
         "name":"AM General",
         "niceName":"amgeneral",
         "manufacturer":null,
         "attributeGroups":{

         }
      },
      ... more ...
   ]
}

$type存在要么.Breeze开发人员做了什么?他写了一个自定义的Breeze JsonResultsAdapter,它位于app/jsonResultsAdapter.js文件中.

我不会在这里重现那个文件,尽管它只有40行.我希望您阅读jsonResultsAdapter文档,下载Edmunds示例,并自己阅读.

我将总结它的作用以及它的工作原理.Breeze jsonResultsAdapter在收到JSON有效负载时会调用您的第一个,并在处理该有效负载中的每个节点时再次调用.您的工作是告诉Breeze如何通过调整节点本身并返回描述节点的元对象来处理您所执行的节点.

这是一个片段:

>if (node.id && node.models) {
    // move 'node.models' links so 'models' can be empty array
    node.modelLinks = node.models;
    node.models = [];
    return { entityType: "Make"  }
}

此代码段中有三个活动:

  1. 识别节点的内容(if ...)
  2. 调整节点值(无论什么原因对你有意义)
  3. 编写并返回"元"对象结果.

专注于#3.这就是开发人员告诉Breeze的地方"将这个节点变成一个Make实体.

结构类型匹配

你可能会说,"Hey Ward,Manufacturer实体类型与JSON对象结构完全匹配.Breeze应该将它识别为Manufacturer."

Breeze不会通过匹配类型结构来实现实体类型.我认为它也不应该......因为不同的类型通常具有相同的结构.例如:我有一个StatusCodeProductCode两个实体类型{ id: int, name: string}.我们还有许多其他改进工作要做; 应对类型歧义在我们的列表中并不高.

命名惯例

最后,让我们回到我看到的另一个问题.

你的configureBreezeManager方法开始了:

breeze.NamingConvention.camelCase.setAsDefault();

您已将默认命名约定从"客户端和服务器上的相同"更改为"pascalCase-on-client/CamelCase-on-server".

通过切换到camelCase约定,您告诉Breeze foo应该将客户端属性发送到服务器Foo.

这是正确的做法吗?如果您的服务器期望CamelCase属性名称.但是,根据JSON有效负载中的属性名称,服务器也需要CamelCase.客户端和服务器上的属性名称相同.如果breeze向制造商发送Name属性值而不是属性值,则会发生错误name.

保持微风默认,"无所事事"约定.不要覆盖它.从你的configureBreezeManager.删除pascalCase约定行.

保存更改

我们一直在讨论查询结果.我们还没有谈到如何将更改保存回服务器.

我确定你有自己的协议(像ReST一样?)和序列化格式.这是一个完全不同的讨论.让我们不要在这个Stack Overflow问题中进入.我只是提醒你,你很快就会对这个问题嗤之以鼻.