为什么我的Breeze.js实体不创建ko.observables? [英] Why are my Breeze.js entities not creating ko.observables?

查看:78
本文介绍了为什么我的Breeze.js实体不创建ko.observables?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using Breeze.js without the server side components and have the entities being created on the client side with the following code. Per Ward's request I have simplified everything and am including more information. My MetaDataStore Configuration function-

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 }
            }
        });
    }

我来自服务器的JSON响应

My JSON response from my server

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

我在datacontext中的配置代码(整个设置减去缺少服务器元数据的过程是在John Papa的课程之后进行设置的)

My config code from my datacontext (the whole setup minus the lack of server metadata is set up after John Papa's courses)

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

当模型被下拉时,数据已经存在,但是数据没有包装在ko.observables中,并且init函数中计算的ko.observables/ko.compute不在从查询中传递出来的模型中.如何确保模型的数据包含在ko.observables中,并添加了ko.computeds?

When the models are pulled down the data is there, but the data is not wrapped in ko.observables and the ko.observables/ko.computed in the init functions are not in the models that are passed out of the queries. How can I ensure the model's data is wrapped in ko.observables and the ko.computeds are added?

推荐答案

此答案既是关于分析问题的教程,又是对问题的答案.

This answer is as much a tutorial on analyzing the problem as answer to the question.

让我们从根本上简化,直到找到缺失的步骤.让我们从您拥有的最简单的实体类型开始...一个实体类型最多具有2到5个属性.没有一个?补一个.将Manufacturer减少为"id"和"name".我们正在尝试首先降低技术难度.

Let's simplify radically until we find the missing step. Let's start with the simplest entity type you have ... one with 2 to 5 properties max. Don't have one? Make one up. Cut down the Manufacturer to just "id" and "name". We're trying to get the mechanics down first.

您没有在服务器上使用任何微风组件.美好的.选择一个提供该测试实体数据的服务器端点.让该端点仅通过一个实例传递一个JSON数组.向我们展示到达客户端的JSON…完整的JSON有效负载,就像它到达网络上一样.应该简短;如果不是这样,那么您还不够简化.

You aren't using any breeze components on the server. Fine. Pick a server endpoint that delivers that test entity data. Have that endpoint deliver a JSON array with just one instance. Show us the JSON that are arriving on the client ... the entire JSON payload, exactly as it arrives on the wire. It should be brief; if it isn't, you haven't simplified enough.

在我们可以确定您是否需要JsonResultsAdapter以及是否需要它时,它会是什么样子.

THEN we can figure out whether you need a JsonResultsAdapter and what it should look like if you do.

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

Show us the exact sequence by which you populate the metadataStore with an EntityType, ctor, and initializer. Frankly, I'd rather have no ctor or initializer until we've got the first one working.

如何确保您创建的EntityManager正在使用该商店?我们需要查看您的配置代码以及如何更新EntityManager并将其用于查询端点.

How do you make sure that the EntityManager you create is using that store? We need to see your configuration code and how you new-up the EntityManager and use it to query the endpoint.

如果您遵循我的建议,则代码不会太多.也许二十行. JSON提要大约应该有10行.如果您无法达到这些数字,则说明您还不够简化.

If you follow my suggestion there won't be much code. Twenty lines maybe. And the JSON feed should be about 10 lines. If you can't hit these numbers, you haven't simplified enough.

现在,您已经对示例进行了重新设计,我对在哪里找到一个更好的主意.

Now that you've re-worked the example, I have a better idea where to look.

两件事突然发生在我身上

Two things leap out at me:

  1. 服务器的JSON结果
  2. camelCase命名约定
  1. The JSON result from the server
  2. The camelCase naming convention

服务器中的JSON

让我们漂亮地打印您提供的JSON结果并进行讨论:

JSON from the Server

Let's pretty print the JSON results you provided and discuss them:


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

Breeze将不知道如何处理该JSON对象,因为它缺少类型信息.轻拂它只是一个任意的对象,可能是投影的结果.

Breeze won't know what to do with that JSON object because it lacks type information. To Breeze its just an arbitrary object, perhaps the result of a projection.

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

Compare that to the JSON result of a query hitting the DocCode Web API. Here's the URL generated for the query:


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

这是(缩写)JSON结果

and here's the (abbreviated) JSON result


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

默认情况下,Breeze客户端期望使用 JSON.NET ,ASP.NET中的默认序列化程序.

By default a Breeze client expects data that have been serialized with JSON.NET, the default serializer in ASP.NET.

JSON.NET负载是一个节点或节点数组. JSON.NET向每个节点添加自己的$id$type属性.

A JSON.NET payload is either a node or an array of nodes. JSON.NET adds its own $id and $type properties to each node.

我想把注意力集中在 $type 属性上,您可以将其识别为.NET类型的全名(带有命名空间的类,程序集名称).

I want to focus your attention on the $type property which you may recognize as the full name (class-with-namespace, assembly-name) of a .NET type.

如果没有$id属性,您可能会逃脱.

You could get away without the $id property.

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

The $id is an auto-incrementing serialization key. Often the same object appears multiple times in a payload. Instead of repeating the contents, JSON.NET substitutes a simple node like {$ref: #} where # refers to the $id of an earlier node. This approach both reduces the payload size and breaks circular references.

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

But Breeze is really looking forward to that $type property. That is how it connects the JSON object/node to a type in your metadata. If your manufacturer example node had one, it might like this:


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

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

I don't know how you're serializing data on your server. It would seem you are using something other than JSON.NET.

那太酷了.我只是在告诉你Breeze在默认情况下是如何工作的. .NET非常友好. 但是Breeze不需要.NET .这是一个纯JavaScript库.您只需要告诉它您想要什么即可.

That is cool. I'm just telling you how Breeze works by default; it is very .NET friendly. But Breeze doesn't need .NET. It is a pure JavaScript library. You just have to tell it what you want.

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

The simplest thing you can do is add toType to your query.


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

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

In this way, you state explicitly that the top level node(s) returned by the 'Manufacturers' endpoint contain data for the Manufacturer type that you described in metadata.

我敢打赌,这会立即为您解决(一旦您解决了下文所述的命名约定问题).

I'll bet this works for you right away (once you fix the Naming Convention problem described below).

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

This is an effective approach but it has several drawbacks. I'll mention two:

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

  1. You have to remember to add it to every query.

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

It only works for top-level entities; if won't work for nested entities such as are returned when you apply the .expand() clause.

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

I prefer to teach the Breeze client how to interpret the JSON results on its own ... with a custom JsonResultsAdapter.

查看> 微风Edmunds示例 ,其中有一个Breeze客户会使用Edmunds车辆信息服务中的数据.

Check out the Breeze Edmunds Sample in which a Breeze client consumes data from the Edmunds Vehicle Information service.

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

The Edmunds server sends a completely different kind of JSON payload in response to queries. Here is a snippet:


{
   "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 中.

No $type there either. What did the Breeze developer do? He wrote a custom Breeze JsonResultsAdapter and it's in the file app/jsonResultsAdapter.js.

尽管只有40行,但我不会在此处复制该文件.我希望您阅读 jsonResultsAdapter文档,并下拉Edmunds样本,然后自己阅读.

I'm not going to reproduce that file here although it is only 40 lines. I want you to read the jsonResultsAdapter documentation, pull down the Edmunds sample, and read it for yourself.

我将总结它的作用及其工作方式. Breeze在接收到JSON有效负载时首先调用jsonResultsAdapter,然后在处理该有效负载中的每个节点时再次调用.通过调整节点本身并返回描述该节点的元对象,您的工作将告诉Breeze如何处理该节点.

I will summarize what it does and how it works. Breeze calls your jsonResultsAdapter first when it receives a JSON payload and again as it processes each node in that payload. Your job is tell Breeze how to treat that node which you do by tweaking the node itself and returning a meta object that describes the node.

这是一个片段:


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

此代码段中包含三项活动:

There are three activities in this snippet:

  1. 识别节点的含义(if ...)
  2. 调整节点值(无论出于何种原因,对您而言都是如此)
  3. 组成并返回元"对象结果.

关注#3 .在那儿,开发人员告诉Breeze将这个节点变成Make实体.

Focus on #3. That's where the developer told Breeze "Turn this node into a Make entity.

您可能会说:沃德,Manufacturer实体类型与JSON对象结构完全匹配.Breeze应该将其识别为Manufacturer."

You might say, "Hey Ward, the Manufacturer entity type matches the JSON object structure exactly. Breeze should recognize it as a Manufacturer."

微风不会通过匹配类型结构来区分实体类型.我也不认为这应该...因为不同类型通常共享相同的结构.例如:我有一个都是{ id: int, name: string}StatusCodeProductCode实体类型.我们还有很多其他的改进要进行.应对类型歧义在我们列表中并不重要.

Breeze does not divine the entity type by matching the type structure. Nor do I think it should ... because different types often share the same structure. For example: I have a StatusCode and ProductCode entity types that are both { id: int, name: string}. We have plenty of other enhancements to work on; coping with type ambiguity is not high on our list.

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

Finally, let's return to the other problem that I saw.

您的configureBreezeManager方法开始:

breeze.NamingConvention.camelCase.setAsDefault();

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

You've changed the default Naming Convention from "same-on-client-and-server" to "pascalCase-on-client/CamelCase-on-server".

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

By switching to the camelCase convention, you're telling Breeze that a client-side property foo should be sent to the server as Foo.

这是正确的做法吗?如果您的服务器需要CamelCase属性名称,则可能是这样.但是,基于JSON有效负载中的属性名称,服务器也希望使用CamelCase.客户端和服务器上的属性名称相同.如果微风发送给制造商一个Name属性值而不是name属性值的东西,将会发生不好的事情.

Is that the right thing to do? It would be if your server expected CamelCase property names. But, based on the property names in your JSON payload, the server expects CamelCase too. The property names are identical on client and server. Bad things will happen if breeze sends a manufacturer with a Name property value instead of a name property value.

保留微风的默认设置,即不执行任何操作"约定.不要覆盖它.从您的configureBreezeManager中删除该pascalCase约定行.

Leave the breeze default, "do nothing" convention in place. Don't override it. Remove that pascalCase convention line from your configureBreezeManager.

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

We've been talking about query results. We haven't talked at all about how you're going to save changes back to the server.

我确定您有自己的协议(类似ReST?)和序列化格式.那是一个完全不同的讨论.我们不要在这个堆栈溢出问题中讨论这个问题.我只是在提醒您,您可能很快就会对这一问题scratch之以鼻.

I'm sure you have your own protocol (something ReST-like?) and serialization format. That is a completely different discussion. Let's not get into that in this Stack Overflow question. I'm just alerting you to the probability that you'll be scratching your head about this one pretty soon.

这篇关于为什么我的Breeze.js实体不创建ko.observables?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆