Является ли общая сборка единственным способом создания объектов из службы WCF REST - PullRequest
1 голос
/ 03 мая 2011

Я пишу приложение, которое использует внутреннюю службу REST на основе WCF, и я признаю, что являюсь новичком в REST. Поскольку я не могу использовать «Добавить ссылку на службу», у меня нет готовых прокси-объектов, представляющих типы возврата из методов службы. Пока что единственный способ, которым я смог работать со службой, - это поделиться сборкой, содержащей типы данных, предоставляемые службой.

Моя проблема с этим расположением состоит в том, что я вижу только две возможности:

  1. Реализация DTO (DataContracts) и предоставление этих типов из моего сервиса. Мне все равно придется делиться сборкой, но этот подход ограничит типы, содержащиеся в сборке, контрактом на обслуживание и DTO. Я не люблю использовать DTO только ради их использования, хотя они добавляют еще один уровень абстракции и время обработки для преобразования из объекта домена в DTO и наоборот. Кроме того, если я хочу иметь бизнес-правила, проверку и т. Д. На клиенте, мне все равно придется делиться объектами домена, поэтому необходима дополнительная сложность.

  2. Поддерживать сериализацию объектов моего домена, выставлять эти типы и делиться этой сборкой. Это позволило бы мне обмениваться бизнес-логикой и логикой проверки с клиентом, но также предоставляет клиенту части моих доменных объектов, предназначенные только для приложения-службы.

Возможно, пример поможет обсуждению ...

  • В моем клиентском приложении отобразится список документов, полученных из службы REST (операция GET). Служба возвращает массив объектов DocumentInfo (облегченное представление документа только для чтения).

  • Когда пользователь выбирает один из элементов, клиент извлекает полный объект Document из службы REST (GET by id) и отображает форму ввода данных, чтобы пользователь мог изменить объект. Нам нужны правила проверки для расширенного пользовательского опыта.

  • Когда пользователь фиксирует изменения, объект Document передается в службу REST (операция PUT), где он сохраняется в внутреннем хранилище данных.

  • Если позволяет состояние документа, пользователь может «опубликовать» документ. В этом случае клиент отправляет запрос в службу REST со значением Document.ID, и служба выполняет операцию, извлекая объект домена Document на стороне сервера и вызывая метод Publish. Метод публикации не должен быть доступен клиентскому приложению.

На мой взгляд, мои объекты Document и DocumentInfo должны быть в общей сборке. Это делает Document.Publish доступным для клиента. Одна идея скрыть это состоит в том, чтобы сделать метод внутренним и добавить атрибут InternalsVisibleTo, который позволяет моему сервисному приложению вызывать метод, а не клиента, но это кажется "вонючим".

Я на правильном пути или что-то упустил?

Ответы [ 3 ]

4 голосов
/ 03 мая 2011

Классы, которые вы используете на сервере, не должны совпадать с классами, которые вы используете на клиенте (кроме как во время самой передачи данных). Наилучшим подходом является создание пакета (сборки / проекта), содержащего DTO, и совместное использование их между сервером и клиентом. Вы упомянули, что вы не хотите создавать DTO ради этого , но это лучшая практика. Влияние на производительность при добавлении дополнительных слоев незначительно, а многоуровневая структура фактически помогает упростить разработку и сопровождение вашего приложения (избегая ситуаций, подобных вашей, когда клиент имеет доступ к серверному коду).

Я предлагаю начать со следующих пакетов:

  1. Сервис: находится только на сервере, предоставляет сервис и содержит логику серверного приложения.
  2. DTO: Находится как на сервере, так и на клиенте. Содержит простые классы, которые содержат данные, которые необходимо передать между сервером и клиентом. Классы не имеют кода, кроме свойств. Это недолговечные объекты, которые выживают достаточно долго только для передачи данных.
  3. Репозиторий: находится только на клиенте. Вызывает сервер и превращает объекты модели в DTO (и наоборот).
  4. Модель: находится только на клиенте. Содержит классы, которые представляют бизнес-объекты и отношения. Объекты модели остаются в памяти на протяжении всего жизненного цикла приложения.

Код вашего клиентского приложения должен вызывать Repository для получения объектов Model (вы можете также рассмотреть возможность просмотра MVVM, если не знаете, как это сделать).

Если ваш сервисный код достаточно сложный, чтобы ему требовался доступ к классам модели, вы должны создать отдельный пакет модели (очевидно, присвоив ему другое имя) - единственные классы, которые должны существовать как на сервере, так и на клиенте, - это классы DTO.

2 голосов
/ 12 мая 2011

Я подумал, что опубликую свой подход, отдавая должное и Грегу, и Джейку за помощь в руководстве по этому пути.

Несмотря на то, что Джейк прав, десериализация данных на клиенте может быть выполнена с любым типом, если он реализует один и тот же контракт данных, однако принудительное выполнение этого без WSDL может быть немного сложным. Я нахожусь в среде, где другие разработчики будут работать с моим решением как для поддержки и поддержки существующих, так и для создания новых клиентов, которые используют мой сервис. Они используются для «добавления справочника услуг» и собираются.

Наиболее полезными были замечания Грега об использовании разных объектов на клиенте и сервере. Я пытался минимизировать дублирование, разделяя мой уровень домена между клиентом и сервером, и это стало причиной моей путаницы. Как только я разделил их на два отдельных приложения и рассмотрел их по отдельности, каждое со своими собственными вариантами использования, картина стала более ясной.

В результате я теперь делюсь сборкой Contracts, которая содержит мои сервисные контракты, так что клиент может легко создать канал к серверу (используя WCF на стороне клиента) и контракты данных, представляющие DTO, передаваемые между клиентом и сервис.

На клиенте у меня есть объекты ViewModel, которые обертывают объекты модели (контракты данных) для пользовательского интерфейса и используют класс агента службы для связи со службой, используя контракты службы из общей сборки. Поэтому, когда пользователь нажимает кнопку «Опубликовать» в пользовательском интерфейсе, контроллер (или команда в WPF / SL) вызывает метод публикации для агента службы, передавая идентификатор документа для публикации. Сервисный агент передает запрос в REST API (операция публикации).

На сервере API REST реализован с использованием тех же сервисных контрактов. В этом случае служба работает с моими доменными службами, репозиториями и доменными объектами для выполнения задач. Поэтому, когда вызывается операция службы публикации, служба извлекает объект домена документа из DocumentRepository, вызывает метод Publish для объекта, который обновляет внутреннее состояние объекта, а затем служба передает обновленный объект в метод Update хранилища. чтобы сохранить изменения.

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

0 голосов
/ 03 мая 2011

Вы можете сериализовать свои доменные объекты и затем десериализовать их в различные типы на клиенте.Оба типа должны реализовывать один и тот же контракт данных.Все сериализуемые типы имеют как минимум контракт данных по умолчанию, который включает все открытые свойства и поля для чтения / записи.

...