返回了EntityTypes中的Breeze EntityQuery缺失数据

时间:2014-02-13 03:50:44

标签: javascript angularjs breeze hottowel

我正在使用Angular和Breeze设置SPA。我一直在从复数网站关注John Papa的热巾教程。我有一个奇怪的问题我认为可能会从我的元数据产生?但最后,我并不确定......

首先,我的API正在运行LAMP堆栈,所以我没有使用EF。我创建了一个元数据端点,我认为它给了我正确的结构。我正在使用breeze.angular.q.js来帮助我从Q到$ q的映射

资源:api / v1 /元数据

{
    "metadataVersion": "1.0.5",
    "dataServices": [
        {
            "serviceName": "api/v1/",
            "hasServerMetadata": true,
            "jsonResultsAdapter": "webApi_default",
            "useJsonp": false
        }
    ],
    "structuralTypes": [
        {
            "shortName": "tracks",
            "namespace": "MyNamespace",
            "dataProperties": [
                {
                    "name": "id",
                    "nameOnServer": "id",
                    "maxLength": 36,
                    "validators": [],
                    "dataType": "Guid",
                    "isPartOfKey": true
                },
                {
                    "name": "title",
                    "nameOnServer": "title",
                    "maxLength": 255,
                    "validators": [],
                    "dataType": "String"
                },
                {
                    "name": "description",
                    "nameOnServer": "description",
                    "maxLength": 0,
                    "validators": [],
                    "dataType": "String"
                }
            ]
        }
    ]
}

示例API返回数据如下所示:

资源:api / v1 / tracks

{
    "data": [
        {
            "id": "495f21d6-adfc-40b6-a41c-fc93d9275e24",
            "title": "harum",
            "description": "Error doloribus ipsam et sunt fugiat."
        },
        {
            "id": "d7b141d2-6523-4777-8b5a-3d47cc23a0fe",
            "title": "necessitatibus",
            "description": "Voluptatem odit nulla maiores minima eius et."
        }
    ],
    "embeds": [
        "courses"
    ]
}

现在用我的所有代码,我实际上是从我的api返回正确的数据。我已经把微风网站上的例子作为我在SO(like this question and great answer from ward)找到的一些好花絮。唉,没有运气。基本上发生的事情是当我尝试在我的视图模型中循环我的结果时,从我的微风查询返回,我得到一个角度误差Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: t in vm.tracks, Duplicate key: object:00I

调用发生在我的datacontext中的一个函数中。我的querySucceeded promise回调中返回的数据似乎没有被正确绑定。

datacontext.js

...
function getTrackPartials() {
    ...
    return EntityQuery.from(entityNames.track)
        .toType(entityNames.track)
        .using(manager).execute()
        .then(querySucceeded, _queryFailed);

    function querySucceeded(data) {
        console.log(data);  // <--  Log out to see what is returned
        tracks = data.results;
        _areTracksLoaded(true)
        log('Retrieved [Track Partials] from remote data source', tracks.length, true);
        return tracks;
    }
}

如果我要将这些数据记录到控制台,我会得到这个(所有的$$ hashKey是相同的,id,title,description都是NULL。但我确实得到了正确的结果数,并且这不是巧合 - 如果我调整我应该接收的结果数量,它每次都正确匹配。

querySucceeded promise callback

现在,由于我的数据有点不同 - 我使用了Edmonds example并创建了自定义JsonResultsAdapter,因此我可以“按摩”数据。目前它非常简陋,因为我只是想让它起作用。真的让我失望的是,如果我从node中的visitNode函数中注销JsonResultsAdapter参数,它就会有正确的数据.... ????

entityManagerFactory.js

(function () {
    'use strict';

    var serviceId = 'entityManagerFactory';
    angular.module('app').factory(serviceId, ['config', emFactory]);

    function emFactory(config) {
        breeze.config.initializeAdapterInstance('modelLibrary', 'backingStore', true);
        breeze.NamingConvention.camelCase.setAsDefault();

        var serviceName = config.remoteServiceName;
        var metadataStore = new breeze.MetadataStore();

        var provider = {
            metadataStore: metadataStore,
            newManager: newManager
        };

        var jsonResultsAdapter = new breeze.JsonResultsAdapter({
            name: "Tracks",
            extractResults: function(json) {
                console.log(json.results.data);  // <--  Log out to see what is returned
                return json.results.data;
            },
            visitNode: function(node, mappingContext, nodeContext) {
                console.log(node);  // <--  Log out to see what is returned
                return {
                    entityType: 'tracks',
                    nodeId: node.id
                };
            }
        });

        var dataService = new breeze.DataService({
            serviceName: serviceName,
            jsonResultsAdapter: jsonResultsAdapter
        });

        return provider;

        function newManager() {
            var mgr = new breeze.EntityManager({
                dataService: dataService,
                metadataStore: metadataStore
            });

            return mgr
        }
    }
})();

这是我的JsonResultsAdapter :: extractResults函数的返回值 JsonResultsAdapter extractResults return value

这是我的JsonResultsAdapter :: visitNode函数中的一个节点 JsonResultsAdapter visitNode node parameter

任何帮助将不胜感激。就像我说的,我不确定错误发生在哪里?但是,如果我不得不猜测,我会说我的EntityQuery使用我的经理和JsonResultsAdapter之间存在一些脱节,这可能是由我生成的错误元数据引起的。

** 更新 **

所以我走过微风代码,弄清楚我在哪里丢失了数据,并且能够弄清楚发生了什么,以及解决问题的方法。但是,我不确定这是否是实际处理此问题的最佳方法。

我应该提一下,我用bower来安装微风 - 通过这样做,我去了bower-breeze-angular git://github.com/eggers/bower-breeze-angular.git包,而不是默认的微风breeze git://github.com/IdeaBlade/Breeze.git,这是一个膨胀的示例和其他数据,我并不热衷包装到我的回购中。

在微风中,在我的JsonResultsAdapter::visitnode回调返回后,它需要“merge”我的数据,我遇到的问题是从我的节点返回的entityKey不匹配起来。这是因为我的mappingContext中的rawValueFn正在寻找nameOnServer,我认为我在我的服务器中设置了元数据 - 但不知怎的,当我退出时我的数据表格已经改变了我的设置。

这是一个dp注销,如果你回顾我的元数据资源调用中的顶部,我特意将其设置为“id”。 这是如何改变为Id的?这是什么导致我的头痛!

DataProperty with changed nameOnServer Key

我可以通过在我的JsonResultsAdapter中的mappingContext上更新我的rawValueFn函数来解决这个问题,一切都会起作用 - 但这感觉就像是“黑客”。我也试过玩“NamingConvention”,但这似乎也不起作用。

这是我更新的JsonFactory,它可以使用

    var jsonResultsAdapter = new breeze.JsonResultsAdapter({
        name: "Tracks",
        extractResults: function(json) {
            return json.results.data;
        },
        visitNode: function(node, mappingContext, nodeContext) {

            // Had to adjust this so it would lowercase and correctly match
            mappingContext.rawValueFn = function(rawEntity, dp) {
                name = dp.name;
                name.substring(0, 1).toLowerCase() + name.substring(1);
                return rawEntity[name];
            }

            return {
                entityType: 'tracks'
            };
        }   
    }); 

1 个答案:

答案 0 :(得分:3)

哇这是一个很长的问题。

首先,我建议您查看"Metadata by hand"主题,该主题描述了使用Breeze Labs元数据助手定义元数据的更简单方法。这将减少很多单调乏味,使阅读和理解更加清晰。

第二次不要在元数据中指定“jsonResultsAdapter”。在我看来,就像你将元数据固定到WebAPI适配器一样,事实上,你想要使用另一个。 不要在元数据中指定“namingConvention”,因为这将胜过您在其他地方设置的内容。鉴于您没有从服务器获取元数据,“hasServerMetadata”应该是false,如果您根本不设置它(您不应该这样做)。

第三次,坚持客户端名称并忘记“nameOnServer”。无论如何NamingConvention都会粉碎。

第四,如果(如图所示)客户端和服务器端属性名称是全部,则camelCase 不要更改NamingConvention默认值!你不想要任何翻译。默认不进行翻译。

如果我对此是正确的,请NamingConvention更改为camelCase! “camelCase”约定告诉Breeze“服务器是PascalCase,因此将我的客户端camel case属性名称转换为服务器上的Pascal名称”。如果我理解正确,您不希望客户端“id”成为服务器端“Id”......这将会发生什么。这就是为什么(我相信)你将“Id”视为“nameOnServer”。

第五,在JsonResultsAdapter内,节点名称与服务器中的JSON匹配,因此是服务器端名称。保持这种方式。当NamingConvention将节点属性值转换为实体属性值时,它们会将它们转换为客户端名称。实际上,如果您错误地在节点上使用客户端名称,则会丢失数据。

当你从服务器到达时,你是否需要改变JSON中的属性名称和值?如果没有,请不要在visitNode方法中弄乱这些名称。关于您需要做的就是确保为节点识别正确的EntityType并将其返回到结果中。

第六次,我很确定visitNode结果的“entityType”属性必须是实际的EntityType不是类型的名称< / strong>正如您在示例中所示。你不能说

return {
   entityType: 'tracks',
};

你必须给它真正的类型(我很确定)

return {
   entityType: trackType,
};

查看其他Breeze适配器(例如,Web API适配器)。它从EntityType获得MetadataStore

第七为什么要设置“nodeId”?我不是说你做错了。但你应该知道为什么。 “nodeId”用于在同一实体在有效载荷中多次出现时帮助重建对象图。只有当某个其他节点中的“nodeRefId”指向“nodeId”值时,它才有用。此时,只有一种实体而不是关系,设置“nodeId”并不能完成任何事情。稍后它会......但只有你用一个有意义的值设置它。

我认为您正在做的是将“nodeId”设置为主键值Track 。这不是你node.id的情况吗?如果我是对的,不要那样做。 “nodeId”是 NOT 您实体的PK。它是用循环序列化实体图的标记。

哇,我的答案很长

我担心你在这里有点过头了。编写dataService适配器或jsonResultsAdapter不是Breeze初学者任务。如果你要去那里,请研究现有的适配器并仔细地遵循它们。知道他们为什么做他们做的事而不是甩开它。

我希望我在这里提供了一些线索。

我怀疑它比你制作它简单得多。一些关键的想法:

  • 确保您没有更改NamingConvention,除非您确实需要更改客户端到服务器的属性名称拼写。

  • 将JsonResultsAdapter中的“entityType”设置为EntityType,而不是类型的名称。

  • 不要重写像rawValueFn这样的微风函数;你将打破微风,你不会知道如何或为什么。