Вопросы, связанные с распределением данных .NET, в стиле CRUD. - PullRequest
4 голосов
/ 29 сентября 2010

Контекст. Создание приложения интеллектуального клиента на платформе .NET, в котором используется сложная модель базы данных с большим количеством столбцов. Естественный стиль приложения - это типичный CRUD, управляемый данными. В некоторых случаях также присутствует немало логики на стороне сервера и несколько сложных проверок. Вы имеете полный контроль над клиентом и сервером, поэтому необходимость взаимодействия как минимум.


В этом вопросе много деталей, извинений за это, но это потому, что я хочу установить правильный контекст для ответов.


Еще несколько предположений
- Как не редкость в мире Microsoft, большинство предыдущих приложений были написаны с использованием DataSets, поэтому это самая известная технология для разработчиков. Но допустим, что разработчики хорошо разбираются и в ОО-мышлении.
- Вам нужно будет выполнить проверки как на клиенте, так и на сервере.
- Вы не показываете большинство данных в табличной форме.
- Это не приложение для интрасети, поэтому о пропускной способности не следует слишком много думать


Самый большой вопрос: наборы данных или объекты?


Если вы выбираете наборы данных, у вас есть несколько положительных и отрицательных значений
- С точки зрения положительных моментов: вы получаете небольшую поддержку Microsoft в плане извлечения данных из базы данных, получения данных по сети и возврата измененных данных по сети меньшими порциями - поскольку вы можете указать только отправку изменений. Отправка меньшего количества данных - это хорошо, так как потенциально может потребоваться совсем немного данных.
- Минусы: с точки зрения валидации, бизнес-логики и т. Д. Вы получаете процедурную форму кода и не получаете преимуществ объектно-ориентированного кода - поведение и данные вместе, более естественный стиль работы и размышлений о что вы делаете, и, возможно, более тесные связи с логикой проверки. Вы также можете отвести взгляд от преимущества размещения набора данных в сетке, поскольку это не обычный случай использования.

Если вы выбираете объекты, это та же самая тренировка, но есть и другие варианты:
Положительные стороны: поведение и данные вместе. Проверка логики ближе. Проще увидеть и понять отношения между объектами. Более читаемый код. Проще для модульного тестирования. Но есть еще несколько вариантов и работа, которую вам необходимо выполнить:


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


Карта контрактов
- Как правило, рекомендуется сопоставлять данные с серверных объектов контрактным объектам, вероятно, DTO. Поскольку это приложение хорошо подходит для архитектуры в стиле CRUD, DTO на самом деле не добавляет значимости картине, а только отображает работу.


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


Независимо от того, выбираете вы добавление слоя контракта или нет, у вас есть большие структуры объектов, которые должны быть отправлены по проводам. Поскольку мы контролируем как клиента, так и сервер, транспорт и кодирование должны быть двоичным кодированием по TCP. Это поможет С наборами данных у вас есть возможность только отправить изменения обратно. Отправка всей структуры объекта назад и вперед является вероятной проблемой производительности. Возможность отправки всего объектаструктура, чтобы как-то идентифицировать вовлеченные изменения (Создать, Обновить, Удалить) и отправить только информацию об этом.Теоретически не так сложно отправить совокупный идентификатор корня на сервер и изменения, попросить сервер лениво загрузить объединенный корень, выполнить внесенные изменения, а затем снова сохранить.Но большая сложность связана с выявлением сделанных изменений.Вы когда-нибудь шли на такой подход?Зачем?Как именно ты это делаешь?

Презентация
Точная технология пользовательского интерфейса не так уж важна для вопроса, возможны WinForms, Silverlight или WPF.Давайте предположим, что мы используем WPF, поскольку это новый умный клиент.Это означает, что у нас есть двухстороннее связывание и мы можем правильно использовать MVVM.

Объекты, связанные с пользовательским интерфейсом, должны будут реализовывать INotifyPropertyChanged и вызывать событие при каждом обновлении свойства.Как вы решаете это?Если вы выберете сценарий общего кода, вы можете добавить его к объектам домена, но это будет включать добавление кода и логики на стороне сервера, которая никогда не предназначена для использования там.Разделение является более естественным, если вы выбираете объекты контракта, но это не так много добавленной стоимости только для добавления слоя отображения.

Технологии
Существует несколько технологий, которые могут помочь в решении некоторых проблем, но часто усложняют другие.Вы ими пользуетесь или сами строите вещи с нуля?**
- CSLA возможен, но он усложняет модульное тестирование и, по-видимому, добавляет более тесную связь с доступом к данным.Это помогает справиться с рядом проблем, но лично я не обладаю компетенцией в этой технологии, поэтому трудно сказать, подходит ли она.
- Услуги WCF RIA были бы возможны для решения Silverlight, ноЕсть определенные ограничения.Размер данных - один.
- WCF Data Services - это еще один подход к быстрому запуску чего-либо, но REST не сильно помогает, и вам также не хватает поддержки валидации, которую вы имеете в RIA Services.

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


Обновление

Спасибо за ответы, ребята!Я пытался задать вопрос достаточно открытым, чтобы открывать для разных ответов, но достаточно конкретным, чтобы справиться с несколькими весьма распространенными требованиями.

Существуют разные соображения, которые имеют разные плюсы и минусы, и которые варьируются от системы к системе.Каждый обычно добавляет сложность поиска решения.Одним из пунктов этих вопросов было получение ответов, в частности, с несколькими дополнительными требованиями, которые не обязательно соответствуют непосредственно одному ответу, который часто является правильным сегодня - с пользовательским интерфейсом на основе задач.Я не "CRUD-парень", если хотите.Но некоторые системы по разным причинам (чаще всего устаревшие) хорошо подходят для CRUD.

Многие бизнес-приложения имеют схожие требования, которые тянутся в разные стороны:

Связанные с бизнесом
- Просмотр: отображение данных для пользователя и обновление тех же данных (чтение и CUD - создание, обновление, Удалить)
- Проверка: бизнес-правила

Связанный с пользовательским интерфейсом
- Проверка: правила пользовательского интерфейса
- Обновления пользовательского интерфейса: код, относящийся только к обновлению пользовательского интерфейса при изменении объекта (INotifyPropertyChanged)

Связано с сетью
- Размер данных: количество данных, отправляемых по проводам

Связано с БД
- Ленивая загрузка

Связано с SRP / повторным использованием
-Сопоставление: вызвано несколькими слоями объектов / разделением проблем

Обслуживание / изменение, связанное
- Изменения: Добавление новой информации (столбцы / поля)
- Количество кода
- Повторное использование и "причины"изменить "

Техническийограничения
- Отслеживание изменений

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

Если бы я попытался обобщить что-то для большинства ситуаций, я бы сказал что-то вроде:

Клиент
- Используйте MVVM для разделения и тестирования
- Создайте виртуальную машину сверхуDTO
- Реализация INotifyPropertyChanged в ВМ.
- Использование XamlPowerToys, Postsharp или некоторых других способов помочь с этим может быть полезным
- Отдельные операции чтения и CUD в UI
- Создание задачи CUD на основеи используйте команды или аналогичные команды для отправки этих операций на серверную сторону

Сервер
- Индивидуальное создание dto для экрана
- ИЛИ используйте подход с несколькими запросами, описанный Ayende в http://msdn.microsoft.com/en-us/magazine/ff796225.aspx
- Используйте автоматическое преобразование, чтобы избежать утомительного, ручного и совершенно не связанного с проблемой, которую вы пытаетесь решить, шаг, который отображает
- Пусть модель предметной области главным образом связана с бизнес-операциями, включая операции, связанные с CUD,и не читается
- Избегайте повторного использования, что увеличивает количество причин для изменения
- Избегайте проблем с инкапсуляцией
- (И тем самым включить архитектуру стиля CQRS и, возможно, раздельное масштабирование операций чтения и CUD во времени)
- Попробуйте найти подход к проверке, который бы хорошо соответствовал тому, что должно быть сделано (Хорошее чтение: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/02/15/validation-in-a-ddd-world.aspx)

Это обязательно подход, который я выбрал бы в этой конкретной ситуации?

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

Ответы [ 2 ]

8 голосов
/ 30 сентября 2010

Я могу ответить только из нашего собственного опыта. Мы пробовали разные фреймворки (WCF RIA, Ideblade) и пришли к выводу, что фреймворки только ухудшат ситуацию. Я объясню ниже.

Прежде всего вы должны забыть о CRUD. CRUD есть только в демо-приложениях - в реальных приложениях такое поведение.

Я не рекомендую имитировать весь граф сущностей на стороне клиента. Это две разные проблемы.

Вы должны создавать специальные Dto для каждого контекста. Например. допустим, у вас есть OrderSearchView, затем вы создаете OrderSearchDto и отображаете только те поля, которые вам нужны. В EditOrderView вы бы вместо этого использовали EditOrderDto, который содержит только те поля, которые вам нужны.

Я бы не советовал использовать инструмент автоматического сопоставления между сущностями и dto. Потому что часто между dto и сущностью нет отношения один-к-одному. Dto часто строится разными сущностями. В любом случае, сопоставление так просто, поэтому я не вижу смысла в структуре сопоставления. И задача не в отображении, а в написании модульного теста, который вам все равно придется выполнять (с или без структуры отображения).

Dtos должен быть независим от технологий на стороне клиента. А внедрение INotifyPropertyChanged в dto нарушает принцип единой ответственности. Существует область, которую они называют объектами передачи данных. Вместо этого вы создаете Presenters на стороне клиента. Вы создаете EditOrderPresenter, который является оберткой вокруг EditOrderDto. Таким образом, dto будет просто закрытым полем члена внутри EditOrderPresenter. Presenter специально создан для редактирования на уровне клиента - поэтому он обычно реализует INotifyPropertyChanged. EditOrderPresenter обычно имеет те же имена свойств, что и dto.

Вы должны физически отделить проверку клиента от проверки сущности на стороне сервера. Остерегайтесь доли! Я думаю, что проверка клиента - это всего лишь настройка GUI - чтобы улучшить работу с графическим интерфейсом. Не стоит особо подчеркивать наличие общего кода проверки между dto и entity - это может вызвать больше головной боли, чем полезности. Просто убедитесь, что вы всегда проверяете на стороне сервера, независимо от того, какой тип проверки выполняется на стороне клиента. Существует два вида проверок: простая проверка свойств и проверка всей сущности (то же самое относится и к dto). Проверка сущности должна выполняться только при переходе между состояниями. Проверьте Джимми Нильссона Домен Управляемый Дизайн для фона знаний. Я не рекомендовал бы использовать механизм правил проверки - просто используйте шаблон состояния.

Тогда как насчет обновлений, вставки, удаления? В наших реализациях мы используем WCF, и API WCF имеет только один метод: IResponse [] Process (params IRequest [] запросы); Что это на самом деле значит ? Это означает, что клиент отправляет пакет запросов на сервер. На стороне сервера вы реализуете RequestHandler для каждого запроса, определенного в системе. Затем вы возвращаете список ответов. Убедитесь, что метод Process () является одной единицей работы (~ одна транзакция). Это означает, что если один из запросов в пакете потерпит неудачу - все они потерпят неудачу - и это вызовет откат транзакции - и не повредит БД. (Не используйте коды ошибок в обработчиках ответов - вместо этого приведите исключение.)

Я бы порекомендовал вам заглянуть на сервер сообщений Agatha. У Дэви Брайона есть отличные посты о слое сообщений. В нашей компании мы решили внедрить собственный сервер обмена сообщениями - потому что нам не нужно было все то, что предлагал Агата, мы сделали некоторые улучшения синтаксиса. В любом случае, внедрение сервера обмена сообщениями не очень сложно - и это хороший опыт обучения. Ссылка http://davybrion.com/blog/

Тогда что вы делаете с Dto.Ну, вы никогда не обновляете их, но изменяете их на стороне клиента, чтобы получить правильную обратную связь с графическим интерфейсом.Таким образом, вы заставляете докладчиков отслеживать все, что происходит с dto (запрос) - в правильном порядке.Это будет ваш запрос.Затем отправьте пакет запроса команде процесса в WCF - тогда запросы будут «воспроизведены» на стороне сервера и обработаны обработчиками запросов.Это на самом деле означает, что вы никогда не обновляете dto.Но докладчики могут отредактировать dto на клиентской стороне, чтобы обеспечить правильную обратную связь.Работа докладчиков также заключается в том, чтобы отслеживать все выполненные изменения, чтобы выдать их обратно на сервер в виде пакета запросов (с запросами в том же порядке, в котором они были отредактированы).Подумайте о следующем сценарии: вы извлекаете существующий порядок, редактируете его, а затем фиксируете изменения обратно в базу данных.Это приведет к двум пакетам, один для получения заказа и один для возврата изменений.
RequestBatch 1: GetOrderByIdRequest

(.. затем пользователь редактирует данные ..)

ReqeuestBatch 2:
StartEditOrderRequest, изменение состояния для редактирования режима, упрощенная проверка
AddConsigneeToOrderRequest
ChangeEarliestETDOnOrderRequest, нет необходимости в повторной проверке, последний ETD еще нетChangeNumberOfUnitsOnOrderlineRequest
EndEditOrderRequest, изменение состояния на исходное состояние, выполните проверку сущности здесь!
GetOrderByIdRequest, чтобы обновить графический интерфейс с последними изменениями.

На стороне сервера мы используем NHibernate.Nhibernate использует кэш первого уровня, чтобы избежать большой нагрузки на БД.Таким образом, все запросы в пределах одной и той же единицы работы (requestbatch) будут использовать кэш.

Каждый запрос должен содержать только минимальный объем данных для выполнения работы.Это означает использование OrderId + некоторые другие свойства вместо всего dto.Что касается оптимистического обновления, вы можете отправить некоторые из старых значений вместе с запросом - это называется набором параллелизма.Помните, что набор параллелизма обычно не содержит много полей.Поскольку порядок обновления, который был изменен за это время, не обязательно означает, что у вас будет условие повышения.Например.добавление и упорядочение строки, в то время как получатель был отредактирован другим пользователем, не означает, что у вас есть условие повышения.

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

Кстати, мы попробовали сервисы WCF RIA в проекте среднего размера.И это пошло не так хорошо.Мы должны были найти способы (взломы) вокруг структуры, чтобы сделать то, что мы хотели.И это также основано на генерации кода - что довольно плохо для сервера сборки.Кроме того, вы никогда не должны делать видимость через слои.Вы должны иметь возможность изменять объекты с резервной копией, не влияя на уровень клиента.С РИА это очень сложно.Я думаю, что OData относится к той же категории, что и WCF RIA.

Если вам нужно создавать запросы на стороне клиента, вы используете шаблон спецификации - не используйте iqueryable - тогда вы будете независимы от внутренних сущностей.

Удачи.
twitter: @ lroal

2 голосов
/ 29 сентября 2010

Интересная проблема:)

Если вы начинаете с нескольких принципов:

  • Попробуйте уменьшить объем данных, передаваемых по проводам
  • Попробуйтеминимизируйте количество времени, затрачиваемое на написание сантехнического кода
  • Попробуйте улучшить тестируемость

Исходя из этого, я бы:

  • Использовал объекты POCO для передачи данных.Наборы данных содержат много информации, которая может вам не понадобиться
  • Использование Entity Framework POCO для доступа к базе данных, экономит ваше сопоставление объектов контракта с объектами данных
  • Поместите проверку в вспомогательные классы, легко тестировать,и поддерживает модель общего кода

В наших проектах мы сэкономили время, используя Entity Framework по сравнению с Enterprise Library и DataSets.

На объектах на стороне сервера и на стороне клиента вы можете попробовать:

  • Объект на стороне клиента наследует объект на стороне сервера и реализует INotifyPropertyChanged
  • Поместите объект на стороне клиента и на стороне сервера в отдельные DLL, чтобы на сервере не было неиспользуемого кода
  • использовать Automapper для отображения между двумя типами.(может быть лучше использовать интерфейсы)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...