Почему мои объекты Breeze.js не создают ko.observables?

Я использую Breeze.js без серверных компонентов и создаю сущности на стороне клиента со следующим кодом. По просьбе Уорда я упростила все и добавляю больше информации. Моя конфигурация 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 }
            }
        });
    }

Мой JSON-ответ от моего сервера

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

Мой код конфигурации из моего datacontext (вся настройка за вычетом отсутствия метаданных сервера настраивается после курсов Джона Папы)

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, а ko.observables/ko.com, представленные в функциях init, отсутствуют в моделях, которые передаются из запросов. Как я могу убедиться, что данные модели упакованы в ko.observables и что ko.computeds добавлены?

1 ответ

Решение

Этот ответ является как учебником по анализу проблемы, так и ответом на вопрос.

Шаг 1 - Упростить и изолировать

Давайте радикально упростим, пока не найдем пропущенный шаг. Давайте начнем с самого простого типа объекта, который у вас есть... один с 2 -5 свойствами макс. Не один? Сделай один. Сократить Manufacturer просто "идентификатор" и "имя". Мы пытаемся сначала разобраться с механикой.

Вы не используете какие-либо компоненты 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 \ /",
  "утверждено": "1",
  "user_id": "3"
}

Breeze не будет знать, что делать с этим объектом JSON, потому что ему не хватает информации о типе. Breeze это просто произвольный объект, возможно, результат проекции.

Сравните это с результатом JSON запроса на веб-API DocCode. Вот URL, сгенерированный для запроса:

> HTTP:// локальный:47595/ ветер / Northwind/ Поставщики / $ топ =1

и вот (сокращенно) результат JSON

[
   {
      "$ ID":"1",
      "$type":"Northwind.Models.Supplier, DocCode.Models",
      "SupplierID":1,
      "CompanyName":"Экзотические жидкости"
   }
]

По умолчанию клиент Breeze ожидает данные, которые были сериализованы с помощью JSON.NET, сериализатора по умолчанию в ASP.NET.

Полезная нагрузка JSON.NET - это либо узел, либо массив узлов. JSON.NET добавляет свой собственный $id а также $type свойства для каждого узла.

Я хочу сосредоточить ваше внимание на $type свойство, которое вы можете распознать как полное имя (класс-с-пространством имен, имя-сборки) типа.NET.

Вы могли бы уйти без $id имущество.

$ Id является автоинкрементным ключом сериализации. Часто один и тот же объект появляется несколько раз в полезной нагрузке. Вместо того, чтобы повторять содержимое, JSON.NET заменяет простой узел, такой как {$ref: #} где # относится к $id более раннего узла. Этот подход уменьшает размер полезной нагрузки и прерывает циклические ссылки.

Но Бриз действительно ждет этого $type имущество. Таким образом он соединяет объект / узел JSON с типом в ваших метаданных. Если ваш пример узла производителя был один, он может выглядеть так:

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

Я не знаю, как вы сериализуете данные на своем сервере. Казалось бы, вы используете что-то другое, чем JSON.NET.

Это круто. Я просто говорю вам, как работает Breeze по умолчанию; это очень.NET дружественный. Но Бриз не нуждается в.NET. Это чистая библиотека JavaScript. Вы просто должны сказать ему, что вы хотите.

Используйте toType(...)

Самое простое, что вы можете сделать, это добавить toType на ваш запрос.

var query = breeze.EntityQuery.from ('Производители')
                  .где( ...)
                  .toType("Производитель");

Таким образом, вы прямо заявляете, что узлы верхнего уровня, возвращаемые конечной точкой "Производители", содержат данные для Manufacturer тип, который вы описали в метаданных.

Могу поспорить, что это работает для вас сразу (как только вы исправите проблему соглашения об именах, описанную ниже).

Это эффективный подход, но он имеет несколько недостатков. Я упомяну два:

  1. Вы должны помнить, чтобы добавить его к каждому запросу.

  2. Это работает только для объектов высшего уровня; if не будет работать для вложенных объектов, которые возвращаются при применении .expand() пункт.

Я предпочитаю учить клиента Breeze, как интерпретировать результаты JSON самостоятельно... с помощью пользовательского JsonResultsAdapter,

Пользовательский JsonResultsAdapter

Ознакомьтесь с примером Breeze Edmunds, в котором клиент Breeze использует данные из службы информации об автомобиле Edmunds.

Сервер Edmunds отправляет совершенно другой вид полезной нагрузки JSON в ответ на запросы. Вот фрагмент:

{
   "MakeHolder":[
      {
         "Идентификатор":200347864,
         "модель": [
            {
               "Ссылка":"/ апи / транспортное средство / ч общее / Хаммер",
               "Идентификатор":"AM_General_Hummer",
               "Название": "Хаммер"
            }
         ],
         "имя":"AM General",
         "NiceName":"amgeneral",
         "Производитель": нулевой,
         "attributeGroups":{

         }
      },
      ... Больше...
   ]
}

нет $type там тоже. Что сделал разработчик Breeze? Он написал пользовательский Breeze JsonResultsAdapter, который находится в файле app / jsonResultsAdapter.js.

Я не собираюсь воспроизводить этот файл здесь, хотя это всего 40 строк. Я хочу, чтобы вы прочитали jsonResultsAdapter документацию, достаньте образец Edmunds и прочитайте его для себя.

Я подведу итог, что он делает и как это работает. Бриз зовет тебя jsonResultsAdapter сначала, когда он получает полезную нагрузку JSON, и снова, когда он обрабатывает каждый узел в этой полезной нагрузке. Ваша задача - рассказать Breeze, как обращаться с этим узлом, что вы делаете, настраивая сам узел и возвращая мета-объект, который описывает узел.

Вот фрагмент:

> if (node.id && node.models) {
    // перемещаем ссылки на "node.models", чтобы "модели" могли быть пустым массивом
    node.modelLinks = node.models;
    node.models = [];
    return { entityType: "Make"  }
}

В этом фрагменте есть три действия:

  1. определить, что это за узел (if ...)
  2. настройка значений узлов (по любым причинам, которые имеют для вас смысл)
  3. составление и возврат результата "мета" объекта.

Сосредоточьтесь на # 3. Вот где разработчик сказал Breeze "Преврати этот узел в Make юридическое лицо.

Структурное соответствие типов

Вы можете сказать: "Эй, Уорд, Manufacturer Тип сущности точно соответствует структуре объекта JSON. Бриз должен признать это как Manufacturer ".

Бриз не угадывает тип объекта путем сопоставления структуры типа. И я не думаю, что это должно... потому что разные типы часто имеют одинаковую структуру. Например: у меня есть StatusCode а также ProductCode типы объектов, которые оба { id: int, name: string}, У нас есть много других улучшений для работы; справиться с неоднозначностью типов не в нашем списке.

Соглашение об именовании

Наконец, давайте вернемся к другой проблеме, которую я видел.

Ваш configureBreezeManager Метод начинается:

 breeze.NamingConvention.camelCase.setAsDefault (); 

Вы изменили соглашение о присвоении имен по умолчанию с "один и тот же на клиенте и сервере" на "pascalCase-on-client/CamelCase-on-server".

Путем переключения на camelCase условно, вы говорите Бриз, что на стороне клиента foo должен быть отправлен на сервер как Foo,

Это правильно делать? Это было бы, если бы ваш сервер ожидал имена свойств CamelCase. Но, основываясь на именах свойств в вашей полезной нагрузке JSON, сервер ожидает и CamelCase. Имена свойств идентичны на клиенте и сервере. Плохие вещи произойдут, если ветер отправит производителя с Name значение свойства вместо name стоимость имущества.

Оставьте бриз по умолчанию, соглашение "ничего не делать" на месте. Не отменяй это. Удалите эту строку соглашения pascalCase из вашего configureBreezeManager,

Сохранение изменений

Мы говорили о результатах запроса. Мы вообще не говорили о том, как вы собираетесь сохранять изменения на сервере.

Я уверен, что у вас есть свой собственный протокол (что-то вроде ReST?) И формат сериализации. Это совершенно другое обсуждение. Давайте не будем вдаваться в это в этом вопросе переполнения стека. Я просто предупреждаю вас о вероятности того, что вы скоро почесаете голову об этом.

Другие вопросы по тегам