Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, stump, Вы писали:
S>>Ничего не делать. Будет ордер с двумя позициями. AddOrderLine — это просто пример. Если бизнес логика не позволяет добавлять позиции в существующий ордер, то такого метода просто не будет в интерфейсе фасада. C>Ничего не делать — нельзя. Скорее всего, придётся откатывать полусформированый заказ. То есть, руками нужно будет выполнять работу базы.
Кто сказал что он "полусформированый"? Я еще раз повторяю — если бизнес логика конкретной системы не позволяет этого делать, такого метода не будет в фасаде. Основной признак бизнес операции — ее транзакционность со всеми сопустствующими ACID характеристиками. И это прежде всего обозначает, что любая бизнес операция переводит систему из одного валидного состояния в другое валидное состояние.
Если вернутся к многострадальному методу AddOrderLine, то он, несомненно, должен оставить ордер в "сформированном" состоянии, т.е. расчитать НДС пересчитать скидки, тоталы, сабтоталы и т.д. и т.п.
Опять же вернемся к исходному вопросу, обновлять граф объектов на клиенте и затем отсылать их на сервер versus использовать DTO только для биндинга UI и вызывать бизнес методы фасада. Пользователь, добявляя позиции в заказ, хочет тут же видеть удержанные налоги, тоталы и субтоталы, а это все бизнес логика. И тут вам приходится либо гонять туда сюда весь граф DTO объектов после каждого добавления строки в заказ, либо тащить бизнес логику на клиента чтоб выполнять все пересчеты тут. Оба варианта проигрывают бизнес методам. Первый, потому что фактически повторяет его, но при этом не выделяет явного интерфейса. Второй, тем что размывает логику по слоям, если вам потребуется клиент WPF, Silverlight или Web Forms вам придется снова и снова переписывать эту логику.
Классическая трехзвенная архиектура:
Smart клиент на WPF
Application Server на .NET
SQL Server
Клиент и сервер общаются через WCF.
На сервере обычный ORM (LINQ to SQL). Удаленные вызовы обслуживаются RemoteFasade-ом.
Типовой метод для запросы данных с сервера
interface RemoteFasade
{
…
ClaimDTO GetOrder(int orderId);
…
}
Метод GetOrder возвращает на клиента граф связанных объектов. В методе GetOrder идет копирование данных из LINQ to SQL в TDO объекты, есть предположение, что для этого можно написать общие методы, принимающие лямбда выражения в качестве аргументов, т.е. такое ручное копирование с использованием общих методов.
Теперь о том, как данные будут приходить обратно от клиента к серверу. На клиенте есть некий ClientContext. После десериализации объектов на клиенте, для каждого DTO объекта текущий ClientContext подписывается на изменения в DTO объекте. Пользователь редактирует данные, изменения попадают в DTO объекты, а ClientContext регистрирует у себя все изменения. После того, как пользователь поработал с данными, изменения, зарегистрированные в ClientContext, отправляются на сервер. На сервере эти изменения попадают в объекты LINQ to SQL.
Замечание: Обычно паттерны Unit of work и Identity Map идут вместе. В данном случае ClientContext фактически реализует Unit of work на клиенте, но Identity Map вроде пока не нужна.
Re: Покритикуйте архитектуру обмена данными для smart клиент
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Метод GetOrder возвращает на клиента граф связанных объектов. В методе GetOrder идет копирование данных из LINQ to SQL в TDO объекты, есть предположение, что для этого можно написать общие методы, принимающие лямбда выражения в качестве аргументов, т.е. такое ручное копирование с использованием общих методов.
Да, такое представляется возможным для автоматизации, но это не самое узкое место.
AP>На клиенте есть некий ClientContext. После десериализации объектов на клиенте, для каждого DTO объекта текущий ClientContext подписывается на изменения в DTO объекте. Пользователь редактирует данные, изменения попадают в DTO объекты, а ClientContext регистрирует у себя все изменения. После того, как пользователь поработал с данными, изменения, зарегистрированные в ClientContext, отправляются на сервер. На сервере эти изменения попадают в объекты LINQ to SQL.
Здесь меня немного пугает обилие кода, связанным с поддержкой сообщений об изменении DTO объектов и довольно плотной подпиской на все сообщения ClientContext-ом. Накопление истории изменений и применение ее на сервере тоже может оказаться громоздким. Если кроме ClientContext-а, кому-то еще будут нужны сообщения об изменениях объектов, то это может окупиться. Если нет — могу предложить вариант, где ClaimDTO и граф объектов являются надстройкой над типизированным DataSet-ом. Если объекты будут хранить состояния в соответствующих DataRow объектах, то никакой подписки на изменения можно вообще не делать и вместо истории изменений на сервере разгребать изменения в переданном DataSet-е. При использовании LINQ to SQL, такой DataSet вообще не обязан повторять схему БД, может содержать любую избыточную информацию и служить буфером для изменений в ClaimDTO или в схеме БД.
Так же могу порекомендовать обратить внимание на "Entity Framework" и его возможность к транспортировке объектов отдельно от контекста с последующим сохранении изменений (здесь). В этом случае сами DTO объекты могут быть переданы на сервер в качестве изменений.
Re: Покритикуйте архитектуру обмена данными для smart клиент
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Классическая трехзвенная архиектура: AP>Smart клиент на WPF AP>Application Server на .NET AP>SQL Server AP>Клиент и сервер общаются через WCF.
AP>На сервере обычный ORM (LINQ to SQL). Удаленные вызовы обслуживаются RemoteFasade-ом.
AP>Типовой метод для запросы данных с сервера AP>interface RemoteFasade AP>{ AP>… AP>ClaimDTO GetOrder(int orderId); AP>… AP>} AP>Метод GetOrder возвращает на клиента граф связанных объектов. В методе GetOrder идет копирование данных из LINQ to SQL в TDO объекты, есть предположение, что для этого можно написать общие методы, принимающие лямбда выражения в качестве аргументов, т.е. такое ручное копирование с использованием общих методов.
AP>Теперь о том, как данные будут приходить обратно от клиента к серверу. На клиенте есть некий ClientContext. После десериализации объектов на клиенте, для каждого DTO объекта текущий ClientContext подписывается на изменения в DTO объекте. Пользователь редактирует данные, изменения попадают в DTO объекты, а ClientContext регистрирует у себя все изменения. После того, как пользователь поработал с данными, изменения, зарегистрированные в ClientContext, отправляются на сервер. На сервере эти изменения попадают в объекты LINQ to SQL.
AP>Замечание: Обычно паттерны Unit of work и Identity Map идут вместе. В данном случае ClientContext фактически реализует Unit of work на клиенте, но Identity Map вроде пока не нужна.
Я бы не советовал вам связываться с Unit of work и отсылкой изменений в DTO на сервер. Во-первых у вас происходит инфляция слоя Application server, он вырождается в голый ORM. Во-вторых, вас ждут большие сложности с синхронизацией клиентских и серверных контекстов, сохранением данных и обеспечением конкурентнго доступа. Когда-то мы пытались реализовать подобную архитектуру (правдв еще на FW 1.0 и remoting), но в итоге отказались от нее.
Вместо этого предлагаю вам подумать о развитии сервиса RemoteFacade. В нем желательно выделить сервисы данных, которые возвращают DTO для организации UI на клиенте и сервисы бизнес-операций. Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе.
Такой подход значительно облегчает как разработку серверной логики (тот же самый Unit of work), так и разработку клиента. Однако он не подходит, если вам необходимо обеспечить работу клиента offline.
Здравствуйте, stump, Вы писали:
S>Вместо этого предлагаю вам подумать о развитии сервиса RemoteFacade. В нем желательно выделить сервисы данных, которые возвращают DTO для организации UI на клиенте и сервисы бизнес-операций. Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе.
1) Remote доступ предполагает максимально маленькое кол-во обращений к RemoteFacade, а если Stump из RemoteFacade делать Service, то это просто капут.
И если клиенский код будет пару сотен раз вызывать AddOrderLine что бы создать один заказ, то такая система будет очень и очень долго работать.
2) Считаю что ни о какой инфляции слоя App Service речи не может быть, так как данный сервис как выполнял свои функции (валидация, добаввление и т.д.) так и выполняет.
Там было написано русским по белому...
Re[2]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, stump, Вы писали:
S>Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе.
Плохая идея. Потребуется распространение транзакции на клиента и куча связаных с этим неприятностей.
Sapienti sat!
Re[3]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, stump, Вы писали:
S>>Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе. C>Плохая идея. Потребуется распространение транзакции на клиента и куча связаных с этим неприятностей.
Никакого распространения транзакций. Одна бизнес операция — одна транзакция.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, stump, Вы писали:
S>>Вместо этого предлагаю вам подумать о развитии сервиса RemoteFacade. В нем желательно выделить сервисы данных, которые возвращают DTO для организации UI на клиенте и сервисы бизнес-операций. Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе. CRA>1) Remote доступ предполагает максимально маленькое кол-во обращений к RemoteFacade, а если Stump из RemoteFacade делать Service, то это просто капут. CRA>И если клиенский код будет пару сотен раз вызывать AddOrderLine что бы создать один заказ, то такая система будет очень и очень долго работать.
Это другой вопрос — вопрос гранулярности операций фасада. С одной строны чем более гранулярен сервис фасада тем он более универсален в использовании. С другой строны меннее гранулярные сервисы позволяют создавать более эффективный UI. Налицо противоречие и тут надо учитывать много факторов, и среди них удобство использования сервиса, его функциональная полнота и (в качестве главного ограничивающего фактора) стек технологий, используемый для удаленного взаимодействия.
Когда в одной из систем мы в качестве транспорта между клиентом и сервером заменили remoting на web сервисы, мы получили падение производительности в 100 раз. Потом был рефакторинг remote facade в строну большей грануляции и мы вернули производительность на исходный уровень.
CRA>2) Считаю что ни о какой инфляции слоя App Service речи не может быть, так как данный сервис как выполнял свои функции (валидация, добаввление и т.д.) так и выполняет.
Может. При таком подходе бизнес логика перетекает на клиента практически помимо воли разработчика Правда это замечают очень поздно.
Здравствуйте, stump, Вы писали:
S>Здравствуйте, C...R...a...S...H, Вы писали:
CRA>>2) Считаю что ни о какой инфляции слоя App Service речи не может быть, так как данный сервис как выполнял свои функции (валидация, добаввление и т.д.) так и выполняет. S>Может. При таком подходе бизнес логика перетекает на клиента практически помимо воли разработчика Правда это замечают очень поздно.
По сути то что предложил AP, от гранулярного RemoteFacade отличается лишь тем, что в первом случае гранулярностью управляет клиент, а во втором — гранулярность задана жестко дизайном RemoteFacade-а. Ни о каком переносе бизнес логики речи не было. То решение, что предложил я (в качестве основы DTO DataSet, либо набор оторванных объектов EntityFramework-а) в какой-то мере склоняет к бизнес логике на стороне клиента, но даже в этом случае будет не сложно держать бизнес логику на сервере.
Можно подход AP воспринимать как Facade над RemoteFacade-ом, где объектом передачи данных является большой граф объектов, который раскладывается на кучу бизнес операций над RemoteFacade-ом с возможностью выполнения их в рамках одной транзакции. Не вижу ничего плохого в этом подходе.
Конфликты состояний контекстов сервера и клиентов критичны не для всех задач. Местами даже не стоит вопрос о синхронизации состояний на клиентах. В частности, AP вообще ни чего не говорил о состоянии на сервере и синхронизации с клиентами. Быть может в этом случае инфляция AppService в DAL приемлема?
Re[5]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, samius, Вы писали:
S>Здравствуйте, stump, Вы писали:
S>>Здравствуйте, C...R...a...S...H, Вы писали:
CRA>>>2) Считаю что ни о какой инфляции слоя App Service речи не может быть, так как данный сервис как выполнял свои функции (валидация, добаввление и т.д.) так и выполняет. S>>Может. При таком подходе бизнес логика перетекает на клиента практически помимо воли разработчика Правда это замечают очень поздно.
S>По сути то что предложил AP, от гранулярного RemoteFacade отличается лишь тем, что в первом случае гранулярностью управляет клиент, а во втором — гранулярность задана жестко дизайном RemoteFacade-а. Ни о каком переносе бизнес логики речи не было. То решение, что предложил я (в качестве основы DTO DataSet, либо набор оторванных объектов EntityFramework-а) в какой-то мере склоняет к бизнес логике на стороне клиента, но даже в этом случае будет не сложно держать бизнес логику на сервере. S>Можно подход AP воспринимать как Facade над RemoteFacade-ом, где объектом передачи данных является большой граф объектов, который раскладывается на кучу бизнес операций над RemoteFacade-ом с возможностью выполнения их в рамках одной транзакции. Не вижу ничего плохого в этом подходе.
Большой граф объектов, говорите. Ну к примеру (с теми же ордерами) есить бизнес рулез который гласит что нельзя для одного клиента иметь более одного заказа в состоянии "не оплачен". Что у нас получается, пользователь долго формирует ордер на клиенте и потом после отправки на сервер всего графа получает фигу в виде сообщения об ошибке, либо мы тащим на клиента логику реализации этого рулеза.
S>Конфликты состояний контекстов сервера и клиентов критичны не для всех задач. Местами даже не стоит вопрос о синхронизации состояний на клиентах. В частности, AP вообще ни чего не говорил о состоянии на сервере и синхронизации с клиентами. Быть может в этом случае инфляция AppService в DAL приемлема?
В этом случае более приемлема не трех а двух уровневая архитектура. Впрочем верно то, что топикстартер ничего об этом не говорил
samius>>По сути то что предложил AP, от гранулярного RemoteFacade отличается лишь тем, что в первом случае гранулярностью управляет клиент, а во втором — гранулярность задана жестко дизайном RemoteFacade-а. Ни о каком переносе бизнес логики речи не было. То решение, что предложил я (в качестве основы DTO DataSet, либо набор оторванных объектов EntityFramework-а) в какой-то мере склоняет к бизнес логике на стороне клиента, но даже в этом случае будет не сложно держать бизнес логику на сервере. S>>Можно подход AP воспринимать как Facade над RemoteFacade-ом, где объектом передачи данных является большой граф объектов, который раскладывается на кучу бизнес операций над RemoteFacade-ом с возможностью выполнения их в рамках одной транзакции. Не вижу ничего плохого в этом подходе.
stump>Большой граф объектов, говорите. Ну к примеру (с теми же ордерами) есить бизнес рулез который гласит что нельзя для одного клиента иметь более одного заказа в состоянии "не оплачен". Что у нас получается, пользователь долго формирует ордер на клиенте и потом после отправки на сервер всего графа получает фигу в виде сообщения об ошибке, либо мы тащим на клиента логику реализации этого рулеза.
Гранулярность в этом случае регулируется клиентом! Если такая фига не устраивает заказчика, то в этом случае клиентский код может быть подправлен без изменений в серверном коде и интерфейса RemoteFacad-а.
Re[4]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, stump, Вы писали:
S>>>Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе. C>>Плохая идея. Потребуется распространение транзакции на клиента и куча связаных с этим неприятностей. S>Никакого распространения транзакций. Одна бизнес операция — одна транзакция.
А тогда будет следующая проблема:
1. Зовём CreateOrder. В отдельной транзакции.
2. Зовём AddOrderLine. В отдельной транзакции.
3. Зовём AddOrderLine. В отдельной транзакции.
4. Зовём AddOrderLine. В отдельной транзакции. Упс. Вылетел constraint violation.
5. Что делать?
6. Кто виноват?
Sapienti sat!
Re[5]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, stump, Вы писали:
S>>>>Для приведенного вами примера с заказами это могут быть сервис данных заказов, с операциями GetOrder, GetOrderList, и сервис операций заказов, с операциями CreateOrder, DeleteOrder, ChangeOrderState, AddOrderLine и т.д. Впрочем физически оба сервиса можно объединить в одном интерфейсе. C>>>Плохая идея. Потребуется распространение транзакции на клиента и куча связаных с этим неприятностей. S>>Никакого распространения транзакций. Одна бизнес операция — одна транзакция. C>А тогда будет следующая проблема: C>1. Зовём CreateOrder. В отдельной транзакции. C>2. Зовём AddOrderLine. В отдельной транзакции. C>3. Зовём AddOrderLine. В отдельной транзакции. C>4. Зовём AddOrderLine. В отдельной транзакции. Упс. Вылетел constraint violation. C>5. Что делать? C>6. Кто виноват?
Ничего не делать. Будет ордер с двумя позициями. AddOrderLine — это просто пример. Если бизнес логика не позволяет добавлять позиции в существующий ордер, то такого метода просто не будет в интерфейсе фасада.
Описанный сценарий вполне применим на практике. Например бизнес правила разрешают добавлять позиции в ордер только пока он не принят в обработку. Если между шагами 3 и 4 другой пользователь изменил состояние ордера мы получим constraint violation или concurent access exception.
Здравствуйте, stump, Вы писали:
S>Ничего не делать. Будет ордер с двумя позициями. AddOrderLine — это просто пример. Если бизнес логика не позволяет добавлять позиции в существующий ордер, то такого метода просто не будет в интерфейсе фасада.
Ничего не делать — нельзя. Скорее всего, придётся откатывать полусформированый заказ. То есть, руками нужно будет выполнять работу базы.
Опять же, полусформированые объекты (особенно оставленые без присмотра) могут портить отчёты и вносить всякие разные глюки.
Sapienti sat!
Re[8]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, stump, Вы писали:
S>Опять же вернемся к исходному вопросу, обновлять граф объектов на клиенте и затем отсылать их на сервер versus использовать DTO только для биндинга UI и вызывать бизнес методы фасада. Пользователь, добявляя позиции в заказ, хочет тут же видеть удержанные налоги, тоталы и субтоталы, а это все бизнес логика. И тут вам приходится либо гонять туда сюда весь граф DTO объектов после каждого добавления строки в заказ, либо тащить бизнес логику на клиента чтоб выполнять все пересчеты тут. S>Оба варианта проигрывают бизнес методам.
Вот тут я не понял. Это третий вариант, когда DTO вообще не используется? Если да, то не рискуем ли мы последовательностью обращений к RemoteFacade получить набор рассогласованных данных (например, для случая, когда на клиенте принимается решение и для принятия этого решения (например, человеком) требуется широкий контекст данных из разных сущностей).
Re[9]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, samius, Вы писали:
S>Здравствуйте, stump, Вы писали:
S>>Опять же вернемся к исходному вопросу, обновлять граф объектов на клиенте и затем отсылать их на сервер versus использовать DTO только для биндинга UI и вызывать бизнес методы фасада. Пользователь, добявляя позиции в заказ, хочет тут же видеть удержанные налоги, тоталы и субтоталы, а это все бизнес логика. И тут вам приходится либо гонять туда сюда весь граф DTO объектов после каждого добавления строки в заказ, либо тащить бизнес логику на клиента чтоб выполнять все пересчеты тут. S>>Оба варианта проигрывают бизнес методам. S>Вот тут я не понял. Это третий вариант, когда DTO вообще не используется? Если да, то не рискуем ли мы последовательностью обращений к RemoteFacade получить набор рассогласованных данных (например, для случая, когда на клиенте принимается решение и для принятия этого решения (например, человеком) требуется широкий контекст данных из разных сущностей).
Немного выше я говорил о сервисе данных, в задачу которого входит поставлять данные для UI. Естественно что он должен поставлять согласованные данные. Кроме того, для трех-уровневых архитектур характерна работа клиента в disconnected режиме (т.е. без поддержания постоянного соединения, типичный пример web клиент), в этом случае отображаемые данные через какое то время неизбежно устаревают и рассогласовываются. Так построены множество систем и в абсолютном большинстве из них это не является проблемой.
Я не совсем понял про "принятие решения", какое воздействие это оказывает на систему? Если решение выливаетя в вызов какого либо бизнес метода фасада, то любой из таких методов обязан валидировать входные параметры где сразу же всплывет факт рассогласования или устаревания данных. Тут нет никакой проблемы. Зайдите в любой интернет магазин, накидайте товаров в корзину, откройте корзину в двух окнах, в однм удалите товары, а в другом попытайтесь изменить количество в уже несуществующих позициях и посмотрите, что произойдет. Ничего страшного не произойдет.
Здравствуйте, stump, Вы писали:
stump>>>Опять же вернемся к исходному вопросу, обновлять граф объектов на клиенте и затем отсылать их на сервер versus использовать DTO только для биндинга UI и вызывать бизнес методы фасада. Пользователь, добявляя позиции в заказ, хочет тут же видеть удержанные налоги, тоталы и субтоталы, а это все бизнес логика. И тут вам приходится либо гонять туда сюда весь граф DTO объектов после каждого добавления строки в заказ, либо тащить бизнес логику на клиента чтоб выполнять все пересчеты тут. stump>>>Оба варианта проигрывают бизнес методам.
samius>>Вот тут я не понял. Это третий вариант, когда DTO вообще не используется? Если да, то не рискуем ли мы последовательностью обращений к RemoteFacade получить набор рассогласованных данных (например, для случая, когда на клиенте принимается решение и для принятия этого решения (например, человеком) требуется широкий контекст данных из разных сущностей).
stump>Немного выше я говорил о сервисе данных, в задачу которого входит поставлять данные для UI. Естественно что он должен поставлять согласованные данные.
Очевидно, что не только данные для UI требуют согласованности, а так же данные, которые принимают участие в бизнес операции. Но данные, полученные путем последовательных обращений к RemoteFacade-у не могут быть гарантированно согласованными. stump>Кроме того, для трех-уровневых архитектур характерна работа клиента в disconnected режиме (т.е. без поддержания постоянного соединения, типичный пример web клиент), в этом случае отображаемые данные через какое то время неизбежно устаревают и рассогласовываются. Так построены множество систем и в абсолютном большинстве из них это не является проблемой.
согласен stump>Я не совсем понял про "принятие решения", какое воздействие это оказывает на систему? Если решение выливаетя в вызов какого либо бизнес метода фасада, то любой из таких методов обязан валидировать входные параметры где сразу же всплывет факт рассогласования или устаревания данных. Тут нет никакой проблемы. Зайдите в любой интернет магазин, накидайте товаров в корзину, откройте корзину в двух окнах, в однм удалите товары, а в другом попытайтесь изменить количество в уже несуществующих позициях и посмотрите, что произойдет. Ничего страшного не произойдет.
Да, ничего страшного не должно произойти. Однако, всплывает сценарий, когда клиенту RemoteFacad-ом выданы рассогласованные данные. Не потому что сервис данных плохой, а потому, что между обращениями к нему система изменила свое состояние. При обращении клиента к бизнес методам сервера, могут быть поданы рассогласованные данные. Ничего серьезного не должно произойти, но фига будет показана пользователю не потому что пользователь слишком долго думал, а потому что сервис данных возвращает необходимые данные последовательно!
Предпочитаю лишний раз сгонять DTO такому поведению системы.
Re[11]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
S>Да, ничего страшного не должно произойти. Однако, всплывает сценарий, когда клиенту RemoteFacad-ом выданы рассогласованные данные. Не потому что сервис данных плохой, а потому, что между обращениями к нему система изменила свое состояние. При обращении клиента к бизнес методам сервера, могут быть поданы рассогласованные данные. Ничего серьезного не должно произойти, но фига будет показана пользователю не потому что пользователь слишком долго думал, а потому что сервис данных возвращает необходимые данные последовательно! S>Предпочитаю лишний раз сгонять DTO такому поведению системы.
По моему, эта проблема, что называется высосана из пальца. Если у вас есть сложный композитный UI вам придется делать насколько запросов и есть возможнсть получить "рассогласованные" данные. Там где это действительно критично применяют распределенные транзакции. Но в том то и дело, что распределенные транзакции на практике применяются очень редко, удовольствие дорогое и масштабируемость решений убивают начисто.
Другой вариант, сделать специальный "острозаточенный" интерфейс для каждого такого UI, который вернет вам все данные all-in-one.
samius>>Да, ничего страшного не должно произойти. Однако, всплывает сценарий, когда клиенту RemoteFacad-ом выданы рассогласованные данные. Не потому что сервис данных плохой, а потому, что между обращениями к нему система изменила свое состояние. При обращении клиента к бизнес методам сервера, могут быть поданы рассогласованные данные. Ничего серьезного не должно произойти, но фига будет показана пользователю не потому что пользователь слишком долго думал, а потому что сервис данных возвращает необходимые данные последовательно!
samius>>Предпочитаю лишний раз сгонять DTO такому поведению системы. stump>По моему, эта проблема, что называется высосана из пальца. Если у вас есть сложный композитный UI вам придется делать насколько запросов и есть возможнсть получить "рассогласованные" данные.
Дело не только в композитном UI. Вообще UI и бизнес операции связаны глубже, чем это может показаться. Например, для оформления покупки хорошо бы передать кроме идентификатора товара еще и цену, которая была показана пользователю. Если с момента запроса цена изменилась, то тогда бизнес логика на сервере сможет отреагировать и предупредить клиента об изменении цены. Это будет лучше, чем если со счета покупателя снимется сумма отличная от той, на которую рассчитывал покупатель (здесь может пахнуть скандалом). И если для магазинов, где цены меняются не каждый день, только по ночам и в полном дауне, это не критично, то для систем, где данные меняются несколько раз на дню эта проблема актуальна.
Таким образом, есть класс систем, где на проведение бизнес-операции влияет не только сигнал поданный с клиента "Хочу", но еще и некий контекст данных, известный клиенту. При том, что отчасти этот контекст формируется сервером, крайне желательно, чтобы данные вунтри контекста были согласованы. stump>Там где это действительно критично применяют распределенные транзакции. Но в том то и дело, что распределенные транзакции на практике применяются очень редко, удовольствие дорогое и масштабируемость решений убивают начисто.
Абсолютно согласен, что распределенные транзакции убивают масштабируемость, кроме того, не всегда доступны. stump>Другой вариант, сделать специальный "острозаточенный" интерфейс для каждого такого UI, который вернет вам все данные all-in-one.
Точно. all-in-one — это как раз половина DTO. All-in-one в одну сторону, + all-in-one в другую сторону уже почти DTO. Почти — потому что я уже не вижу четкой грани между r/o DTO и all-in-one-ом либо между r/w all-in-one-ом и обычным DTO.
Бывает, что часть контекста меняется на клиенте перед обращением к бизнес операции; бывает, что вся бизнес операция проводится на стороне клиента, сервер же только фиксирует ее.
Надуманно? Думаю, что нет. Опять таки, зависит от требований. Топикстартер пропал, ну да и ладно с ним, мне самому интересен любой опыт на эту тему.
Сам работаю над системой с ОООООООООООООгромным all-in-one в сторону клиента (если брать частями, то это даже дорого в плане производительности, счет идет на минуты) + солидное изменение на стороне клиента + в прогнозах валидация на AppServer-е, а пока просто фиксация изменений. В общем, соображения в пользу DTO.
Re[8]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, stump, Вы писали:
S>Если вернутся к многострадальному методу AddOrderLine, то он, несомненно, должен оставить ордер в "сформированном" состоянии, т.е. расчитать НДС пересчитать скидки, тоталы, сабтоталы и т.д. и т.п.
Ок. У нас сделана система, которая раз в 30 минут печатает на бумаге все outstanding orders и посылает их на почту. Пользователь добавляет первую строку, вторую, третюю. Тут врубается демон и отсылает полусформированый заказ. Упс.
Опять же, каждое добавление строки или другое изменение — это сетевой вызов. А это тормоза интерфейса, особенно если вызов идёт через Инет по медленным каналам. Ну и нагрузка на сервер, конечно.
Ещё другая деталь. Ладно, мы отослали AddOrderLine, и сервер пересчитал суммы, налоги и скидки. Но как эти изменения отобразить клиенту? Что он должен получить из метода AddOrderLine? Весь изменённый граф объектов или просто void?
Sapienti sat!
Re[9]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, stump, Вы писали:
S>>Если вернутся к многострадальному методу AddOrderLine, то он, несомненно, должен оставить ордер в "сформированном" состоянии, т.е. расчитать НДС пересчитать скидки, тоталы, сабтоталы и т.д. и т.п. C>Ок. У нас сделана система, которая раз в 30 минут печатает на бумаге все outstanding orders и посылает их на почту. Пользователь добавляет первую строку, вторую, третюю. Тут врубается демон и отсылает полусформированый заказ. Упс.
Данная проблема решается с помощью выбора грануляции бизнес методов удаленного интерфейса и не является препятствием к использованию подхода, предлагаемого stump-ом. Достаточно изменить сигнатуру так, чтобы все OrderLine-ы ушли одним вызовом.
C>Ещё другая деталь. Ладно, мы отослали AddOrderLine, и сервер пересчитал суммы, налоги и скидки. Но как эти изменения отобразить клиенту? Что он должен получить из метода AddOrderLine? Весь изменённый граф объектов или просто void?
Тут рулят требования к системе. Если проведение бизнес операции на клиенте не допустимо в принципе, то тогда клиенту в любом случае придется запрашивать обновленные данные. Результатом операции так же может быть диагностическая информация (аки результат валидации на сервере). И только в том случае, когда проведение бизнес операции проводится на клиенте а сервер только лишь фиксирует ее, можно не посылать изменения с сервера на клиент. В общем, тут на принятие решения влияют более высокие материи, нежели эффективность передачи графа объектов.
Re[9]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, stump, Вы писали:
S>>Если вернутся к многострадальному методу AddOrderLine, то он, несомненно, должен оставить ордер в "сформированном" состоянии, т.е. расчитать НДС пересчитать скидки, тоталы, сабтоталы и т.д. и т.п. C>Ок. У нас сделана система, которая раз в 30 минут печатает на бумаге все outstanding orders и посылает их на почту. Пользователь добавляет первую строку, вторую, третюю. Тут врубается демон и отсылает полусформированый заказ. Упс.
Пользователь может редактировать заказ час и два. Сохраниять его и опять редакторовать, и так может продолжаться днями и неделями. Я в курсе...
Давайте больше не будем играть в эту игру: вы придумываете новые требования, а я придумываю как их реализовать.
Совершенно очевидно, что в нормальной системе пользователю не дадут вводить позиции в заказ ушедший в обработку, а система не станет обрабатывать заказы которые редактируются пользователями.
Если вы не в курсе, как решаются подобные проблемы, изучайте профессиональную литературу и открытые источники в интернете.
К тому же подобная проблема стоит перед разработчиком независимо от того, какой тип фасада он использует.
C>Опять же, каждое добавление строки или другое изменение — это сетевой вызов. А это тормоза интерфейса, особенно если вызов идёт через Инет по медленным каналам. Ну и нагрузка на сервер, конечно.
Да, этот аспект оказывает влияние на проектирование интерфейса, его уже обсуждали здесь.
C>Ещё другая деталь. Ладно, мы отослали AddOrderLine, и сервер пересчитал суммы, налоги и скидки. Но как эти изменения отобразить клиенту? Что он должен получить из метода AddOrderLine? Весь изменённый граф объектов или просто void?
Возможен любой вариант, все зависит от совокупности требований.
Здравствуйте, samius, Вы писали:
>Здесь меня немного пугает обилие кода, связанным с поддержкой сообщений об изменении DTO объектов и довольно плотной подпиской на все сообщения ClientContext-ом.
Есть предположение, что можно написать вот такой базовый класс для DTO
public class DataTransferObjectBase<TLinqToSqlObject>
{
protected T GetValue<T>(Expression<Func<TLinqToSqlObject, T>> expression)
{
//TODO: implement thisthrow new NotImplementedException();
}
protected void SetValue<T>(Expression<Func<TLinqToSqlObject, T>> expression, T value)
{
//TODO: implement thisthrow new NotImplementedException();
}
protected IEnumerable<T> GetValue<T>(
Expression<Func<TLinqToSqlObject, EntitySet<T>>> expression) where T : class
{
//TODO: implement thisthrow new NotImplementedException();
}
}
пример конкретного DTO
public class OrderDTO : DataTransferObjectBase<Order>
{
public DateTime? OrderDate
{
get
{
return GetValue(x => x.OrderDate);
}
set
{
SetValue(x => x.OrderDate, value);
}
}
public Customer Customer
{
get
{
return GetValue(x => x.Customer);
}
set
{
SetValue(x => x.Customer, value);
}
}
public IEnumerable<OrderDetail> OrderDetails
{
get
{
return GetValue(x => x.OrderDetails);
}
}
}
Сообщения об изменениях, на которые подписывается ClientContext, будут реализованы в базовом классе DataTransferObjectBase. Помимо этого в базовом классе реализуется логика управления связями между DTO объектами, например, OrderDTO.Customer, OrderDTO.OrderDetails.
Второй вариант -- сделать DTO автогенеренными.
>Накопление истории изменений и применение ее на сервере тоже может оказаться громоздким.
Если говорить схематично, история изменений будет накапливаться в виде простой структуры ("Тип LINQtoSQL объекта", "Значение Primary Key-я", "Имя свойства", "Значение свойства"). Возможно, потребуется передача значений всех свойств. Кроме того, будут аналогичные схемы для insert и delete. Еще timpstamp-ы. Но все это будет выполнятся в инфраструктурных классах, и вроде не так уж это сложно получается.
>Если нет — могу предложить вариант, где ClaimDTO и граф объектов являются надстройкой над типизированным DataSet-ом.
Мы рассматривали возможность использования DataSet-ов в качестве DTO. DataSet все-таки отличается от графа связанных объектов. В случае графа -- если объекты графа теряют ссылку на какой-либо объект, то он уже не пренадлежит этому графу, и его уберет сборщик мусора. В случае DataSet-а такого нет -- DataSet жестко хранит все DataRow в себе. Пример, в котором это проявляется, это сценарий с Lookup-ом. Lookup -- это поле на форме, значение поля можно выбирать из списка, список может быть большим, возможно, с пейджингом (например, список продуктов). Если пользователь меняет значение Lookup-а, то в случае графа объекта мы просто выставляем новое значение
orderItem.Product = newProduct
Новый продукт присоединился к графу, старый ушел к сборщику мусора. newProduct берется из списка, который показывается пользователю, либо по Id подгружается с сервера.
В случае с DataSet-ом, у нас проблемы. Мерджить DataSet-ы? или не иметь свойство Product у orderItem, и работать с двумя датасетами и выдергивать Product из второго DataSet-а по Id?
ClientContext (лучше его назвать просто ChangesTracker) не накладывает такой жесткой связи на множество объектов как DataSet. Между DataSet-ом и DataRow двухстороння связь -- DataSet знает о DataRow, и DataRow знает о DataSet. В случает ClientContext-а -- DtoObject хранит ссылку на ClientContext (точнее на делегат из ClientContext), а ClientContext не хранит ссылку на DtoObject.
>При использовании LINQ to SQL, такой DataSet вообще не обязан повторять схему БД, может содержать любую избыточную информацию и служить буфером для изменений в ClaimDTO или в схеме БД.
Схему типизированного DataSet-а каждый раз рисовать вручную?
Re[3]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Alexander Polyakov, Вы писали:
>>Накопление истории изменений и применение ее на сервере тоже может оказаться громоздким. AP>Если говорить схематично, история изменений будет накапливаться в виде простой структуры ("Тип LINQtoSQL объекта", "Значение Primary Key-я", "Имя свойства", "Значение свойства"). Возможно, потребуется передача значений всех свойств. Кроме того, будут аналогичные схемы для insert и delete. Еще timpstamp-ы. Но все это будет выполнятся в инфраструктурных классах, и вроде не так уж это сложно получается.
Я вот не пойму никак, а зачем вам все это? Зачем накапливать историю изменений в клиентском контексте в таком странном виде? Когда ее передавать на сервер? Что делать если свойство объекта изменили пять раз подряд и в конце оно приобрело то-же значение, что и до изменений? Что делать если объект добавили в клиентском контексте, а потом сохранили на него ссылки в других объектах, а потом удалилив клиентском контексте, как вы будете разруливать ссылочную целостность при сохранении на сервере? Почему не прердавать сами измененные объекты? У вас в системе кроме данных никакой логики нет? Что если при изменении свойства OrderDTO.Customer система должна отправить email определенного содержания?
По моему вы сейчас на ровном месте создаете себе кучу проблем, которые потом будете героически преодолевать.
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали:
>>Здесь меня немного пугает обилие кода, связанным с поддержкой сообщений об изменении DTO объектов и довольно плотной подпиской на все сообщения ClientContext-ом. AP>Есть предположение, что можно написать вот такой базовый класс для DTO
AP> public class DataTransferObjectBase<TLinqToSqlObject>
AP> {
AP> }
AP>
Написать то можно, будет ли он работать с LINQ2SQL — вот в чем вопрос! Точнее работать то будет, но будут определенного характера проблемы при попытке сериализации LINQ объектов. Даже если привязать к ним атрибут [Serializable], воспользовавшись partial объявлениями, все равно эти объекты содержат несериализуемые поля типа EntitySet<T>. Еще раз повторюсь, обратите внимание на EntityFramework, его объекты сериализуемы по определению! Кроме того, их можно передавать обратно на сервер и там аттачить к контексту с применением всех изменений. Очень может быть, что их и оборачивать не придется.
AP> public class OrderDTO : DataTransferObjectBase<Order>
AP> {
AP> public DateTime? OrderDate
AP> {
AP> get
AP> {
AP> return GetValue(x => x.OrderDate);
return this.LinqObject.OrderDate; // так не проще?
AP> }
AP> }
AP> public Customer Customer
public CustomerDTO Customer
Вообще, все объекты, которые можно получить из методов DTO объекта, должны быть DTO объектами. Иначе при обращении к свойствам orderDto.Customer.*** не будет происходить фиксации изменений в истории.
AP> {
А здесь придется иметь дело с IdentityMap-ом
AP> get
AP> {
AP> return GetValue(x => x.Customer);
AP> }
AP> set
AP> {
AP> SetValue(x => x.Customer, value);
AP> }
AP> }
AP> public IEnumerable<OrderDetail> OrderDetails
public IEnumerable<OrderDetailDTO> OrderDetails
AP> {
AP> }
AP> }
AP>
AP>Сообщения об изменениях, на которые подписывается ClientContext, будут реализованы в базовом классе DataTransferObjectBase. Помимо этого в базовом классе реализуется логика управления связями между DTO объектами, например, OrderDTO.Customer, OrderDTO.OrderDetails.
Не связывался бы я на вашем месте с сообщениями у каждого экземпляра DTO. Раз уж на то пошло, то проще привязать DTO к контексту, чтобы он сам записывал в контекст свои изменения. А сообщения сделать у контекста.
AP>Второй вариант -- сделать DTO автогенеренными.
Сначала нужно решить все принципиальные проблемы. Одна небольшая деталь (типа несериализуемости LINQ объекта) и вся концепция в корзине!
>>Накопление истории изменений и применение ее на сервере тоже может оказаться громоздким. AP>Если говорить схематично, история изменений будет накапливаться в виде простой структуры ("Тип LINQtoSQL объекта", "Значение Primary Key-я", "Имя свойства", "Значение свойства"). Возможно, потребуется передача значений всех свойств. Кроме того, будут аналогичные схемы для insert и delete. Еще timpstamp-ы. Но все это будет выполнятся в инфраструктурных классах, и вроде не так уж это сложно получается.
не советую связываться с timestamp-ами, особенно в распределенной системе. Даже если все машины в одном домене с общим time сервером, будут проблемы. Лучше счетчики изменений. А еще лучше отказаться от истории.
>>Если нет — могу предложить вариант, где ClaimDTO и граф объектов являются надстройкой над типизированным DataSet-ом. AP>Мы рассматривали возможность использования DataSet-ов в качестве DTO. DataSet все-таки отличается от графа связанных объектов. В случае графа -- если объекты графа теряют ссылку на какой-либо объект, то он уже не пренадлежит этому графу, и его уберет сборщик мусора. В случае DataSet-а такого нет -- DataSet жестко хранит все DataRow в себе. Пример, в котором это проявляется, это сценарий с Lookup-ом. Lookup -- это поле на форме, значение поля можно выбирать из списка, список может быть большим, возможно, с пейджингом (например, список продуктов). Если пользователь меняет значение Lookup-а, то в случае графа объекта мы просто выставляем новое значение AP>Новый продукт присоединился к графу, старый ушел к сборщику мусора. newProduct берется из списка, который показывается пользователю, либо по Id подгружается с сервера. AP>В случае с DataSet-ом, у нас проблемы. Мерджить DataSet-ы? или не иметь свойство Product у orderItem, и работать с двумя датасетами и выдергивать Product из второго DataSet-а по Id?
Вы хотите мержить графы объектов и обновлять тем самым граф DTO по частям? В общем случае это возможно только без физического удаления записей. Да и вообще проблем это создаст больше, чем вы решите.
Вечных DTO лучше не делать. Они должны регулярно обновляться полностью, старый DTO соберет GC, на чем бы он ни был написан. Одним DTO на все задачи вы не обойдетесь. И мержить их не надо. В одном DTO накачены данные очередной страницы списка, при переходе к бизнес-задачам заказываете другой DTO с данными для этой задачи. Иначе для педжинга придется пол базы данных джойнить, а это не есть гуд. AP>ClientContext (лучше его назвать просто ChangesTracker) не накладывает такой жесткой связи на множество объектов как DataSet. Между DataSet-ом и DataRow двухстороння связь -- DataSet знает о DataRow, и DataRow знает о DataSet. В случает ClientContext-а -- DtoObject хранит ссылку на ClientContext (точнее на делегат из ClientContext), а ClientContext не хранит ссылку на DtoObject.
Это ничего не решает, т.к. без IdentityMap-а вам похоже не обойтись. Либо выкидывать отработавшие DTO полностью, либо мудрить со слабыми ссылками, но дело это не благодарное.
>>При использовании LINQ to SQL, такой DataSet вообще не обязан повторять схему БД, может содержать любую избыточную информацию и служить буфером для изменений в ClaimDTO или в схеме БД. AP>Схему типизированного DataSet-а каждый раз рисовать вручную?
Да, это нормально. Или уже сейчас ничего вручную не пишется кроме генераторов? Тесты за Вас тоже генерируются?
stump>По моему вы сейчас на ровном месте создаете себе кучу проблем, которые потом будете героически преодолевать.
Согласен. Тоже от этого страдаю, правда со возрастом все меньше
Re[4]: Покритикуйте архитектуру обмена данными для smart кли
Да, извиняюсь, я немного поторопился. Естественно, DTO объекты ссылаются только на DTO объекты. Схема становиться менее красивой, появляются касты
public class DataTransferObjectBase<TLinqToSqlObject>
{
protected T GetValue<T>(Expression<Func<TLinqToSqlObject, T>> expression)
{
//TODO: implement thisthrow new NotImplementedException();
}
protected void SetValue<T>(Expression<Func<TLinqToSqlObject, T>> expression, T value)
{
//TODO: implement thisthrow new NotImplementedException();
}
protected IEnumerable<DataTransferObjectBase<T>> GetValue<T>(
Expression<Func<TLinqToSqlObject, EntitySet<T>>> expression) where T : class
{
//TODO: implement thisthrow new NotImplementedException();
}
protected DataTransferObjectBase<T> GetRef<T>(Expression<Func<TLinqToSqlObject, T>> expression)
{
//TODO: implement thisthrow new NotImplementedException();
}
protected void SetRef<T>(
Expression<Func<TLinqToSqlObject, T>> expression, DataTransferObjectBase<T> value)
{
//TODO: implement thisthrow new NotImplementedException();
}
}
public class OrderDTO : DataTransferObjectBase<Order>
{
public DateTime? OrderDate
{
get
{
return GetValue(x => x.OrderDate);
}
set
{
SetValue(x => x.OrderDate, value);
}
}
public CustomerDTO Customer
{
get
{
return (CustomerDTO)GetRef(x => x.Customer);
}
set
{
SetRef(x => x.Customer, value);
}
}
public IEnumerable<OrderDetailsDTO> OrderDetails
{
get
{
return GetValue(x => x.OrderDetails).Cast<OrderDetailsDTO>();
}
}
}
public class CustomerDTO : DataTransferObjectBase<Customer>
{
}
public class OrderDetailsDTO : DataTransferObjectBase<OrderDetailsDTO>
{
}
Замечу, что методы принимают Expression, а не делегаты.
Объекты LINQtoSQL не обязаны быть сериализуемыми, классы LINQtoSQL выступают только как типы, инстансов LINQtoSQL объектов в DTO нет.
Re[5]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Замечу, что методы принимают Expression, а не делегаты. AP>Объекты LINQtoSQL не обязаны быть сериализуемыми, классы LINQtoSQL выступают только как типы, инстансов LINQtoSQL объектов в DTO нет.
Тогда, если честно, я не догоняю, откуда будет браться на клиенте значение OrderDTO.OrderDate и куда складываться? И все остальое...
Re[6]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, samius, Вы писали:
S>Тогда, если честно, я не догоняю, откуда будет браться на клиенте значение OrderDTO.OrderDate и куда складываться? И все остальое...
Все складывается во внутренний дикшенери класса DataTransferObjectBase. Референсы и соответствующие Id-шники ведут себя так:
1. Ни какой лайзи подгрузки нет.
2. Если выставляется Id-шник, то соответствующая референс выдает либо эксепшен, либо null (еще не решил что). Старое значение референса сбрасывается.
3. Если выставляется референс, соответствующий Id-шник тоже меняется.
Re[7]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Все складывается во внутренний дикшенери класса DataTransferObjectBase. Референсы и соответствующие Id-шники ведут себя так: AP>1. Ни какой лайзи подгрузки нет. AP>2. Если выставляется Id-шник, то соответствующая референс выдает либо эксепшен, либо null (еще не решил что). Старое значение референса сбрасывается. AP>3. Если выставляется референс, соответствующий Id-шник тоже меняется.
Оу, внутренний словарь? Чего ради тогда эквилибристика с Expression-ами и типами LINQtoSQL объектов?
Re[8]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, samius, Вы писали:
S>Оу, внутренний словарь? Чего ради тогда эквилибристика с Expression-ами и типами LINQtoSQL объектов?
Ради проверок на этапе компиляции. Например,
public DateTime? OrderDate
{
get
{
return GetValue(x => x.OrderDate);
}
set
{
SetValue(x => x.OrderDate, value);
}
}
x => x.OrderDate указывает на свойство LINQ объекта, контролируется имя свойства и тип свойства, например, x => x.OrderDate и value должны быть одного типа.
Ну и интелесенс тоже работает.
Re[9]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали:
S>>Оу, внутренний словарь? Чего ради тогда эквилибристика с Expression-ами и типами LINQtoSQL объектов? AP>Ради проверок на этапе компиляции. AP>x => x.OrderDate указывает на свойство LINQ объекта, контролируется имя свойства и тип свойства, например, x => x.OrderDate и value должны быть одного типа. AP>Ну и интелесенс тоже работает.
А как из словаря будет получено значение? И как оно в него попадет? (прошу прощения, не силен пока в expression-ах, не перешли еще на vs2008).
Re[10]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
S>А как из словаря будет получено значение?
В пробной реализации ключами в словаре будут имена свойств, из expression имя свойства можно получать вот так
((MemberExpression)expression.Body).Member.Name
Да, здесь заточка на экспрешенны только такого вида obj => obj.Property1, иначе исключение в рантайме.
Далее возможно оптимизация, использовать не имя свойства, а порядковый номер, кешировать откомпилированные лямбда выражения и т.д.
S>И как оно в него попадет? (прошу прощения, не силен пока в expression-ах, не перешли еще на vs2008).
Грубо говоря лямбда выражения могут выступать в двух эпостасях:
1. Как замена анонимным делегатам
2. Как System.Linq.Expressions. Тогда компилятор делает AST, по которому можно ходить.
Я использую второй вариант.
Re[11]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали: S>>И как оно в него попадет? (прошу прощения, не силен пока в expression-ах, не перешли еще на vs2008). AP>Грубо говоря лямбда выражения могут выступать в двух эпостасях: AP>1. Как замена анонимным делегатам AP>2. Как System.Linq.Expressions. Тогда компилятор делает AST, по которому можно ходить. AP>Я использую второй вариант.
Понятно. Reflection... Хотя, можно использовать его только для заполнения карты делегатов для доставания и складывания значений из свойств LINQ2SQL объектов в словари и обратно (т.е. для работы на сервере)... Но там все может упереться в какую-нибудь проблему типа получения в runtime делегата на Generic метод. В .net 2.0 beta2 это еще работало, в релизе уже не знаю, кажется были изменения на эту тему.
Я все-таки еще раз предложу в качестве основы DTO использовать DataSet (нетипизированный), обращаться к его строкам можно так же как и к словарю, с той разницей, что датасет будет один на все DTO, а словарей будет по одному на каждого... Здорово сэкономите на сериализации (DTO объекты можно будет вообще не сериализовывать, а выращивать на переданном датасете прямо на клиенте), заодно получите всю историю изменений, причем токлько изменения и ничего больше (DataSet.GetChanges()).
Или EntityFramework. Его объекты сериализуемы и не требуют никаких словарей, датасетов и прочего. Кажется, повторяюсь.
Или в конце концов нагенерировать нужный код (Emit или CodeDOM). Писать большие вещи на них муторно, но использовать в сочетании с базовыми классами, написанными руками, довольно просто. Совсем не обязательно emit-ить в продакшн рантайме. Можно, например, создать утилиту, которая загрузит сборку с типами Linq2Sql, после чего анализируя их Reflection-ом наэмитит сериализуемые DTO классы с полями (а не словарем), наэмитит код копирования свойств из DTO в Linq2Sql и обратно (для работы сервера), а потом все это сохранит в dll, которую можно будет использовать при разработке. То же самое касается CodeDOM. Здесь главное — собрать хороший прототип для генерации кода.
Впрочем, если подвиги все же важнее, то не буду мешать
Re[12]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
S>Понятно. Reflection... Хотя, можно использовать его только для заполнения карты делегатов для доставания и складывания значений из свойств LINQ2SQL объектов в словари и обратно (т.е. для работы на сервере)...
А что плохого в "закешированном" Reflection-e? Проблема пефоманса решается кешированием. Расходы памяти на кеш это мелочи при сегодняшних объемах памяти. Под "закешированном" Reflection-ом я понимаю глобальный на все приложение словарь, из которого по паре ("тип объекта", "имя свойства") можно достать делегат, который дергает геттор/сеттор свойства. Словарь формируется с использованием Dynamic.cs отсюда http://code.msdn.microsoft.com/csharpsamples/Release/ProjectReleases.aspx?ReleaseId=8
Пример использования можно посмотреть здесь http://www.fikrimvar.net/lestirelim/?p=15
S>Я все-таки еще раз предложу в качестве основы DTO использовать DataSet (нетипизированный), обращаться к его строкам можно так же как и к словарю, с той разницей, что датасет будет один на все DTO, а словарей будет по одному на каждого... Здорово сэкономите на сериализации (DTO объекты можно будет вообще не сериализовывать, а выращивать на переданном датасете прямо на клиенте), заодно получите всю историю изменений, причем токлько изменения и ничего больше (DataSet.GetChanges()).
Да, это неплохой подход, и я очень хорошо его знаю, выполнял проект с использованием этого подхода. Давайте поговрим конкретнее. В предыдущем посте я описал конкретный сценарий с лукапом. Как этот сценарий реализовывается на DataSet-ах?
>что датасет будет один на все DTO, а словарей будет по одному на каждого...
в коде словарь тоже только один в базовом классе DataTransferObjectBase. Инстонсов словаря конечно много, в DataRow данные храняться тоже в виде словаря.
S>Или EntityFramework. Его объекты сериализуемы и не требуют никаких словарей, датасетов и прочего. Кажется, повторяюсь.
Да, надо посмотреть.
S>Впрочем, если подвиги все же важнее, то не буду мешать
Нет, нет, никаких подвигов, чистый прагматизм. Мне не известна нормальная платформа для написания Smart client-а, это меня несколько удивляет... Поэтому приходится самим ваять. Вы не мешаете
Re[13]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>А что плохого в "закешированном" Reflection-e? Проблема пефоманса решается кешированием. Расходы памяти на кеш это мелочи при сегодняшних объемах памяти. Под "закешированном" Reflection-ом я понимаю глобальный на все приложение словарь, из которого по паре ("тип объекта", "имя свойства") можно достать делегат, который дергает геттор/сеттор свойства.
Примеры посмотрю на днях, щас не до них. S>>Я все-таки еще раз предложу в качестве основы DTO использовать DataSet (нетипизированный), AP>Да, это неплохой подход, и я очень хорошо его знаю, выполнял проект с использованием этого подхода. Давайте поговрим конкретнее. В предыдущем посте я описал конкретный сценарий с лукапом. Как этот сценарий реализовывается на DataSet-ах?
Вас интересует подгрузка нового продукта и внедрение его в старый граф? Вообще, на сколько мне известно, DTO не предполагает сценариев докачки данных. Обычно, с сервера заказываются новые DTO графы, которые полностью подменяют старые. На мой взгляд смешивание графов объектов, полученных с разных сессий, может привести к непредсказуемым последствиям и трудноотлаживаемым багам. Хотя, Merge датасетов не исключен.
>>что датасет будет один на все DTO, а словарей будет по одному на каждого... AP>в коде словарь тоже только один в базовом классе DataTransferObjectBase. Инстонсов словаря конечно много, в DataRow данные храняться тоже в виде словаря.
В виде словаря, но не в словаре. Во всяком случае, сериализация DataSet-ов одна из самых шустрых и компактных в Framework-е. Со словарем специально не сравнивал. В случае 1000 словарей против датасета с 1000 строк делаю ставку на датасет. Но дело не в этом, датасет — это сама история в чистом виде, которую не надо писать, а надо только разгребать на сервере! И то, если пользоваться дедовскими адаптерами ADO.NET, то и разгребать не надо, а прямо сливать в BD. Но похоже это уже не популярно
Re[2]: Покритикуйте архитектуру обмена данными для smart кли
Здравствуйте, stump, Вы писали:
>Я бы не советовал вам связываться с Unit of work и отсылкой изменений в DTO на сервер. >Вместо этого предлагаю вам подумать о развитии сервиса RemoteFacade.
Как Unit of work на клиенте противоречит развитию фасада? У вас методы GetOrder, GetOrderList что-то возвращаю? граф объектов (будем их называть DTO), верно? например, GetOrder покрасней мере возвращает сам объект Order и коллекцию OrderItems (а еще неплохо бы для каждого элемента возвращать Product, и все это за один вызов).
Введение терминов “сервисы данных” и “сервисы бизнес-операций” это конечно хорошо. Но какие бы термины вы не вводили RemoteFacade это всего лишь набор методов со своими аргументами. И весь вопрос в том, аргументы каких типов имеют методы RemoteFacade-а и значения каких типов возвращают (кроме примитивных типов)?
Я утверждаю, что методы возвращают DTO, а в качестве аргументов принимают примитивные типы и набор изменений (которые накопились в данном ClientContext-е). Структура набора изменений наружу никак не видна, изменения просто применяются к серверным объектам.
>обеспечением конкурентнго доступа
да, это непростой вопрос, но все же его можно попытаться решить отдельно, не затрагивая преимущества Unit of work-а на клиенте.
Re[14]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
>Вас интересует подгрузка нового продукта и внедрение его в старый граф?
Меня интересует полный цикл работы с Lookup-ом (желательно описать все подробно).
1. на форме у каждого OrderItem-ам надо отображать Description продукта. Description продукта находится в том же DataSet-е или в отдельном? и в какой DataTable?
2. что происходит при смене продукта у одно OrderItem-а?
>Вообще, на сколько мне известно, DTO не предполагает сценариев докачки данных. Обычно, с сервера заказываются новые DTO графы, которые полностью подменяют старые. На мой взгляд смешивание графов объектов, полученных с разных сессий, может привести к непредсказуемым последствиям и трудноотлаживаемым багам. Хотя, Merge датасетов не исключен.
Вы предлагаете гонять весь заказ на сервер и обратно, когда меняется продукт у одного из OrderItem-ов?
Re[15]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали:
>>Вас интересует подгрузка нового продукта и внедрение его в старый граф? AP>Меня интересует полный цикл работы с Lookup-ом (желательно описать все подробно).
Я толком проблему не представляю, но все же попробую ниже. AP>1. на форме у каждого OrderItem-ам надо отображать Description продукта. Description продукта находится в том же DataSet-е или в отдельном? и в какой DataTable?
DataSet обязан быть тот же. Таблица — не принципиально, если есть в датасете relation. AP>2. что происходит при смене продукта у одно OrderItem-а?
Ну вот тут и попробую. Проблема в том, что при редактировании требуется подгрузка данных, и не ясно, как ее реализовать в случае с реализацией на DataSet? Проблема не только DataSet-а. Если брать общий случай, то даже с графами объектов потребуется заказывать новый граф с сервера, потом извлекать из графа объекты и вставлять их в существующий граф. Случай с OrderItem и Product, возможно этого не подразумевает (я не знаю ваших таблиц и связей), но общий случай требует мержа (кстати, в общем случае связей может быть много). Работать с двумя графами объектов, как и с двумя DataSet-ами, не удобно, это может привести к невнятному коду и ошибкам на пустом месте.
Как вариант, можно запросить на сервере новый DTO, который содержит все нужные данные (и те что есть, и те что надо подгрузить). Но как быть, если в существующем DTO уже начались изменения? Отправлять его на сервер в незаконченном виде нельзя.
В таком случае есть, как минимум, два решения:
1) Пойти в сторону UnitOfWork и учиться мержить графы объектов, либо низлежащие DataSet-ы. И то и другое требует сноровки, не исключены конфликты.
2) Разделить бизнес-объекты и DTO. Точнее даже не разделить их (они и так должны быть разделены), а не сливать их вместе ради экономии (Persistence Ignorance, устаревшее POCO, оригинал — POJO). Тогда логика клиента, оперируя PI объектами не будет влиять на переданные с сервера DTO никаким образом ровно до того момента, когда придет время зафиксировать операцию. Тогда PI объекты можно будет отобразить в DTO графе и отправить на сервер изменения (в Вашем случае историю изменения DTO при отображении в него изменений в PI объектах). Я полагаю, что именно это и есть чистое использование DTO. Здесь он не смешивается с UOW. Еще раз повторюсь, DTO не будет принимать никаких изменений до тех пор, пока не потребуется зафиксировать операцию. Соответсвтенно, его можно безопасно мержить (в случае с DataSet встроенными методами), не боясь конфликтов, можно выкидывать и заменять новым... Можно перед совершением отображения PI2DTO заказать последний DTO с сервера для анализа на конфликты (теперь угроза конфликтов появится здесь).
Это потребует отделения логики от персистентности и транспортировки. Готовы Вы пойти на это? Палка о двух концах. Оно вроде бы так и надо, но там как правило всплыавет ворох проблемм. Зато все тестируется на ура.
Вообще, еще принято отделять объекты представления от бизнес-модели. Т.е. у представления, бизнес-модели и DataServices (для двухзвенки) свои собственные типы, которые друг на друга маппируются. В случае со Smart client-ом тут как раз зависит от места проведения бизнес-операций. Похоже, что в Вашем случае, бизнес операция проводится на клиенте, а сервером только фиксируется, т.е. получился удаленный DAL. А значит место DTO объектов между объектами бизнес-модели и удаленным DAL-ом.
Все это хорошо, но эта полная схема может оправдать себя наверное только в больших проектах с большим периодом разработки и с долгой поддержкой. К сожалению, я только читал об этом {здесь}, сам не принимал участие в разработках с таким подходом, но очень бы хотелось. В книге выявлено очень много граблей на этом пути, как всегда, никаких универсальных рецептов, сплошные компромиссы.
>>Вообще, на сколько мне известно, DTO не предполагает сценариев докачки данных. Обычно, с сервера заказываются новые DTO графы, которые полностью подменяют старые. На мой взгляд смешивание графов объектов, полученных с разных сессий, может привести к непредсказуемым последствиям и трудноотлаживаемым багам. Хотя, Merge датасетов не исключен. AP>Вы предлагаете гонять весь заказ на сервер и обратно, когда меняется продукт у одного из OrderItem-ов?
Нет, гонять туда его нельзя. Разве что если предусмотреть бизнес-операцию "Сохранить черновик заказа". Но это не выход, выдумывать промежуточные состояния системы каждый раз когда появляется угроза слить недосформированные данные.
Re[16]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
>Я толком проблему не представляю, но все же попробую ниже. >(я не знаю ваших таблиц и связей)
Каких конкретно знаний вам не хватает?
>Если брать общий случай
Давайте пока разберемся с конкретным случаем Lookup-а. а потом попытаемся обобщить.
> Как вариант, можно запросить на сервере новый DTO, который содержит все нужные данные (и те что есть, и те что надо подгрузить). Но как быть, если в существующем DTO уже начались изменения? Отправлять его на сервер в незаконченном виде нельзя.
Почему же нельзя? Отправить DTO на сервер, на сервере перелить данные из DTO в серверные объекты, провести необходимое действие (выставить новый Product) с серверными объектами (на сервере и Identity Map есть, и Lazy loading). В базу не сограняем. Выгрузить новый граф DTO. Это вариант идеологически совпадает с обычными Web Forms (там данные передаются в HTML-е, а в нашем случае в виде графа DTO). Но гоняются данные всей формы при смене значения в одном Lookup поле. Получаем вопрос, насколько критично так часто гонять весь заказ на сервер?
>В случае со Smart client-ом тут как раз зависит от места проведения бизнес-операций. Похоже, что в Вашем случае, бизнес операция проводится на клиенте, а сервером только фиксируется, т.е. получился удаленный DAL.
Нет, нет, я не собираюсь все расчеты и другие операции (workflow и т.д.) проводить на клиенте. Но проводить все операции (в первую очередь расчеты) на сервере тоже не выход, пользователь будет сильно удивлен, если для расчета количества дней между двумя датами идет удаленный вызов, и пользователь вынужден ждать (удаленные вызовы это почти всегда заметная задержка). С другой стороны для части расчетов требуются данные (из справочников), которые находятся в базе, а на клиенте их нет. Причем какие именно данные зависит от того, по какой ветке пойдет расчет, т.е. от конкретных значений полей, в таком сценарии удобна Lazy Loading. Устраивать Lazy Loading через Интернет это плохо скажется на производительности.
Таким образом, удобный для пользователя вариант:
1. Расчеты, не требующие большого количества данных из базы, выполнять на клиенте.
2. Расчеты, требующие большого количества данных, выполнять на сервере. Также на сервере выполняется движение по workflow, подготовка регистров для отчетов и т.д.
> Можно перед совершением отображения PI2DTO заказать последний DTO с сервера для анализа на конфликты (теперь угроза конфликтов появится здесь).
Какую проблему решает перенос проблемной ситуации с сервера на клиента?
> Тогда логика клиента, оперируя PI объектами не будет влиять на переданные с сервера DTO никаким образом ровно до того момента, когда придет время зафиксировать операцию.
Описанный вами сценарии работы с данными безотносительно в каких объектах они находятся:
1. Данные пришли с сервера
2. Пользователь поработал с пришедшими данными
3. Данные (изменения) отправились на сервер
Имхо, на этот сценарий и ориентированны DataSet-ы.
Но на самом деле общий сценарий сложнее
1. Пользователь запросил первую порцию данных
2. Посмотрел, поизменял данные
3. Затребовал вторую порцию данных.
4. Посмотрел на обе порции данных вместе. Поизменял первую порцию.
5. Повторил шаги 3-4 для следующей порции данных
6. Данные отправились на сервер
В совсем общем сценарии на 4-ом шаге пользователь может менять данные из второй порции. Но очень часто бизнес укладывается в описанную схему (основной момент -- данные из второй порции только для чтения). На мой взгляд инфраструктура для описанной схемы сейчас востребована.
Если вернуться к Order-ам -- Product в OrderItem-е только для чтения, поэтому я не вижу ничего плохо в том, что на клиенте могут оказаться две копии одного продукта. Скорее всего можно мириться даже с ситуацией, когда кто-то сумел поменять продукт, и у нас на клиенте в разных местах формы отображается две разные версии одного продукта. Хотите увидеть полностью актуальные данные сохраняйте и обновляйте всю форму. Поскольку ситуация редкая, а гонять данный каждый раз тоже плохо, то с этим можно мириться, имхо.
Re[17]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали:
>>Я толком проблему не представляю, но все же попробую ниже. >>(я не знаю ваших таблиц и связей) AP>Каких конкретно знаний вам не хватает?
Я не знаю, будет ли Product единственным объектом, подгружаемым с сервера по изменению Lookup-а. Если подгружаемый Pruduct один, то мерж будет тривиальный. Если Product связан с другими сущностями, которые могут быть уже подкачаны на клиент, то мерж усложняется, не зависимо от основы DTO (DataSet или граф объектов).
>> Как вариант, можно запросить на сервере новый DTO, который содержит все нужные данные (и те что есть, и те что надо подгрузить). Но как быть, если в существующем DTO уже начались изменения? Отправлять его на сервер в незаконченном виде нельзя. AP>Почему же нельзя?
Отправлять для сохранения нельзя, если мержить — то можно, но по сути переносить на сервер мерж нет смысла. AP>Отправить DTO на сервер, на сервере перелить данные из DTO в серверные объекты, провести необходимое действие (выставить новый Product) с серверными объектами (на сервере и Identity Map есть, и Lazy loading).
Если на сервере есть Identity Map, почему ее не может быть на клиенте, раз она все равно есть. AP>В базу не сограняем. Выгрузить новый граф DTO. Это вариант идеологически совпадает с обычными Web Forms (там данные передаются в HTML-е, а в нашем случае в виде графа DTO).
Все же есть грань между ViewState и объектами модели. AP>Но гоняются данные всей формы при смене значения в одном Lookup поле. Получаем вопрос, насколько критично так часто гонять весь заказ на сервер?
Только Вы можете ответить, на сколько критично это будет для Вашего проекта. А так же на вопрос "зачем гонять данные всей формы?", и "что из данных формы можно не гонять?".
AP>...Таким образом, удобный для пользователя вариант: AP>1. Расчеты, не требующие большого количества данных из базы, выполнять на клиенте. AP>2. Расчеты, требующие большого количества данных, выполнять на сервере. Также на сервере выполняется движение по workflow, подготовка регистров для отчетов и т.д.
Если здесь значимы только нефункциональные требования, то вполне логично.
>> Можно перед совершением отображения PI2DTO заказать последний DTO с сервера для анализа на конфликты (теперь угроза конфликтов появится здесь). AP>Какую проблему решает перенос проблемной ситуации с сервера на клиента?
Конкретно для мержа графов и анализа конфликтов — избавляет от необходимости переноса и анализа состояния клиента (в том числе невалидного промежуточного) на сервер.
>> Тогда логика клиента, оперируя PI объектами не будет влиять на переданные с сервера DTO никаким образом ровно до того момента, когда придет время зафиксировать операцию. AP>Описанный вами сценарии работы с данными безотносительно в каких объектах они находятся: AP>1. Данные пришли с сервера AP>2. Пользователь поработал с пришедшими данными AP>3. Данные (изменения) отправились на сервер AP>Имхо, на этот сценарий и ориентированны DataSet-ы. AP>Но на самом деле общий сценарий сложнее AP>1. Пользователь запросил первую порцию данных AP>2. Посмотрел, поизменял данные AP>3. Затребовал вторую порцию данных. AP>4. Посмотрел на обе порции данных вместе. Поизменял первую порцию. AP>5. Повторил шаги 3-4 для следующей порции данных AP>6. Данные отправились на сервер
Если не бояться конфликтов и перемешиваний объектов разных графов, что может повлечь передачу нескольких графов на сервер вместо одного, то можно использовать одновременно неограниченное кол-во порций. Имхо, это игра в наперстки с самим собой.
AP>Если вернуться к Order-ам -- Product в OrderItem-е только для чтения, поэтому я не вижу ничего плохо в том, что на клиенте могут оказаться две копии одного продукта. Скорее всего можно мириться даже с ситуацией, когда кто-то сумел поменять продукт, и у нас на клиенте в разных местах формы отображается две разные версии одного продукта. Хотите увидеть полностью актуальные данные сохраняйте и обновляйте всю форму. Поскольку ситуация редкая, а гонять данный каждый раз тоже плохо, то с этим можно мириться, имхо.
Представьте ситуацию: расплачиваясь за покупку, Вы видите одну сумму, но с карточки снимается другая сумма. Либо Вы покупаете один товар, но доставляют другой... Неприятно, но по замыслу разработчика виноваты именно Вы! Надо было чаще обновлять форму!
Да, можно с этим мириться или нет, решать будет разработчик (если в функциональных требованиях это не оговорено). Только в основе решения должно лежать не то, что плохо лишний раз гонять данные, а то, в каком положении может оказаться пользователь системы.
Re[18]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
>Представьте ситуацию: расплачиваясь за покупку, Вы видите одну сумму, но с карточки снимается другая сумма. Либо Вы покупаете один товар, но доставляют другой... Неприятно, но по замыслу разработчика виноваты именно Вы! Надо было чаще обновлять форму!
Я такого не говорил. Решение этой проблемы давно всем известно, и smart client тут не причем. Данные типа цены товара копируются в заказ.
>Да, можно с этим мириться или нет, решать будет разработчик (если в функциональных требованиях это не оговорено). Только в основе решения должно лежать не то, что плохо лишний раз гонять данные, а то, в каком положении может оказаться пользователь системы.
Разработчик должен выполнять оба требования и производительность, и отсутствие таких казусов. Причем оба требования обязательные. Если приложение будет тормозить, то им даже не начнут пользоваться. А если будут редкие казусы, то могут не сразу отказаться, а просто заасайнить багу .
Re[18]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
>Я не знаю, будет ли Product единственным объектом, подгружаемым с сервера по изменению Lookup-а.
Допустим один объект Product.
>Если подгружаемый Pruduct один, то мерж будет тривиальный.
Тривиальный говорите? а если Product с таким идентификатор уже присутствует в датасете (присоединен к другому OrderItem) и отличается от вновь пришедшего, что будет делать мердж датасетов? там какие-то флажки в аргументах кажется есть? Старый продукт тоже останется в датасете, и, возможно, будет мешаться при следующих мерджах. А в случае простого графа без Identity Map мерджа вообще нет ! Причем, если мы наткнулись на редкую ситуация, когда с сервера приходит обновленная версия продукта, то для пользователя это выгладит вполне естественно – часть формы обновилось, а часть нет. А в случае с датасетами вы делаете непонятную для пользователя операции -- мердж. Кстати, вы еще должны корректно обновить пользовательский интерфейс, чтобы отобразить все что вы там намерджели, а датасеты не поддерживают натификпацию.
>Если Product связан с другими сущностями, которые могут быть уже подкачаны на клиент, то мерж усложняется, не зависимо от основы DTO (DataSet или граф объектов).
Повторюсь, в случае графа мерджа нет.
Re[19]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали:
>>Представьте ситуацию: расплачиваясь за покупку, Вы видите одну сумму, но с карточки снимается другая сумма. Либо Вы покупаете один товар, но доставляют другой... Неприятно, но по замыслу разработчика виноваты именно Вы! Надо было чаще обновлять форму! AP>Я такого не говорил. Решение этой проблемы давно всем известно, и smart client тут не причем. Данные типа цены товара копируются в заказ.
Такие данные копируются в заказ для того чтобы сервер мог проверить соответсвие этих данных. Но я говорил о том, что данные, показываемые пользователю хотя бы на одном скрине, не должны вызывать противоречий. Это к ситуации, когда на клиенте оказывается более одной актуальной копии продукта Разработчик знает, где у него в программе самая актуальная копия (или догадывается), а неискушенный пользователь запутается, если на экране будет противоречивая информация.
>>Да, можно с этим мириться или нет, решать будет разработчик (если в функциональных требованиях это не оговорено). Только в основе решения должно лежать не то, что плохо лишний раз гонять данные, а то, в каком положении может оказаться пользователь системы. AP>Разработчик должен выполнять оба требования и производительность, и отсутствие таких казусов. Причем оба требования обязательные. Если приложение будет тормозить, то им даже не начнут пользоваться. А если будут редкие казусы, то могут не сразу отказаться, а просто заасайнить багу .
Этот вопрос не стоит обсуждать заочно. Возможно что накладные расходы на передачу данных малы по сравнению с удаленным вызовом + обращением к БД. Кстати, это Ваше предложение (гнать данные для мержа на сервер).
Re[19]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Здравствуйте, samius, Вы писали:
>>Если подгружаемый Pruduct один, то мерж будет тривиальный. AP>Тривиальный говорите? а если Product с таким идентификатор уже присутствует в датасете (присоединен к другому OrderItem) и отличается от вновь пришедшего, что будет делать мердж датасетов?
Смержит AP>там какие-то флажки в аргументах кажется есть?
Какие такие флажки в аргументах? AP>Старый продукт тоже останется в датасете, и, возможно, будет мешаться при следующих мерджах.
Если установлены ключевые поля, то при мерже обновится соответствующая запись, новой создано не будет. AP>А в случае простого графа без Identity Map мерджа вообще нет !
В случае, когда Product возвращается с сервера одним экземпляром — то обойдется только подменой ссылки, но если Product связан с чем-то еще — мержить графы будет не просто. AP>Причем, если мы наткнулись на редкую ситуация, когда с сервера приходит обновленная версия продукта, то для пользователя это выгладит вполне естественно – часть формы обновилось, а часть нет.
Вам решать, естественно ли это. Может в Вашем случае нужно чтобы пользователь одновременно видел несколько версий продуктов? AP>А в случае с датасетами вы делаете непонятную для пользователя операции -- мердж. Кстати, вы еще должны корректно обновить пользовательский интерфейс, чтобы отобразить все что вы там намерджели, а датасеты не поддерживают натификпацию.
Граф объектов для пользователя такой же темный лес как DataSet с мержем! Не будете же Вы пользователям разжевывать механику подмены ссылок? Нотификацию DataSet-ы поддерживают, да и вызывать Refresh у соответствующих контролок — тоже не проблема.
>>Если Product связан с другими сущностями, которые могут быть уже подкачаны на клиент, то мерж усложняется, не зависимо от основы DTO (DataSet или граф объектов). AP>Повторюсь, в случае графа мерджа нет.
Предположим, что Product ссылается на Manufacture. И в новой порции данных они идут парой. В старой порции есть и Product и Manufacture, причем на тот экземпляр Manufacture ссылаются другие Product-ы. Если вас не будет пугать наличие в одном графе нескольких экземпляров одной Manufacture и одного Product, то придется графы мержить. И даже в случае, когда Product один, нет гарантии, что заменой одной ссылки в графе Вы полностью исключите наличие в графе других экземпляров того же продукта. В общем случае придется проанализировать ВСЕ места, где будут ссылки на данный конкретный Product, который Вы хотите в графе заменить.
Как будет называться такая операция, если не merge? А общий случай мержа графов объектов запросто по сложности не уступит реализации GC!
Re[20]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
>Нотификацию DataSet-ы поддерживают
Вы уверены, что после мерджа DataSeet-ов всё корректно обновиться на UI-е на WPF? (речь про нотификацию шла именно в этом контексте, а не просто абстрактная нотификация. Конкретно WPF не упоминался, но механизм доставки данных как раз и не должен быть сильно завязан на технологию UI-я).
Re[20]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, samius, Вы писали:
AP>>Тривиальный говорите? а если Product с таким идентификатор уже присутствует в датасете (присоединен к другому OrderItem) и отличается от вновь пришедшего, что будет делать мердж датасетов? >Смержит
да мердж мерджит . Но я задавал вопрос в надежде услышать детали, как он это делает.
AP>>там какие-то флажки в аргументах кажется есть? >Какие такие флажки в аргументах?
Это я задавал вопрос . Я имел ввиду PreserveChanges http://msdn.microsoft.com/en-us/library/aszytsd8.aspx
>В случае, когда Product возвращается с сервера одним экземпляром — то обойдется только подменой ссылки, но если Product связан с чем-то еще — мержить графы будет не просто.
Предлагаю иногда спускаться с абстрактных вершин и приводить конкретные примеры для подтверждения своих утверждений. Например, если Product связан с ProductCategory, то все так же просто, просто меняется ссылка.
>мержить графы будет не просто
Никто и не говорил, что будет просто в абсолютном смысле , сейчас мы сравниваем два подхода DataSet-ы и простой граф объектов (ссылки объектов друг на друга).
>Вам решать, естественно ли это. Может в Вашем случае нужно чтобы пользователь одновременно видел несколько версий продуктов?
В каком в моем случае? Во многих проектах (если не во всех), в которых я учавствовал(ю), да это приемлемо.
>Вам решать, естественно ли это. Может в Вашем случае нужно чтобы пользователь одновременно видел несколько версий продуктов?
Какая-то детсадовская отговорка, типа у вас больше знаний о проект, и только вы можете выбрать правильное решение. У вас разве с опытом не накапливаются экспертные знания, с помощью которых, задав пару тройку вопросов, вы уже можете предложить правильное решение?
>Граф объектов для пользователя такой же темный лес как DataSet с мержем! Не будете же Вы пользователям разжевывать механику подмены ссылок?
О графе объектов пользователь, конечно, не задумывается, но вот ссылку на продукт в строке заказа он вполне ясно себе представляет (помогает ему в этом то, что происходит на экране).
>Не будете же Вы пользователям разжевывать механику подмены ссылок?
Смена значения в Lookup-е на пользовательском интерфейсе хорошо согласуется со сменой ссылки в строке заказа (в объектах). Поэтому дополнительные объяснения не требуются.
>Предположим, что Product ссылается на Manufacture. И в новой порции данных они идут парой. В старой порции есть и Product и Manufacture, причем на тот экземпляр Manufacture ссылаются другие Product-ы. Если вас не будет пугать наличие в одном графе нескольких экземпляров одной Manufacture и одного Product, то придется графы мержить. И даже в случае, когда Product один, нет гарантии, что заменой одной ссылки в графе Вы полностью исключите наличие в графе других экземпляров того же продукта. В общем случае придется проанализировать ВСЕ места, где будут ссылки на данный конкретный Product, который Вы хотите в графе заменить.
Как будет называться такая операция, если не merge? А общий случай мержа графов объектов запросто по сложности не уступит реализации GC!
Тк я ж про это и говорил, объекты под Lookup-оп обычно только для чтения, поэтому несколько экземпляров таких объектов не вызывают проблем.
При работе с DataSet-ами у меня всегда возникала мысль о том, что они навязывают способ мышления , т.е. ты идешь уже не от того, что надо пользователю, а от того что тебе предлагают DataSet-ы. И ты постоянно занимаешься тем, чтобы всунуть требования пользователя в датасетный подход.
Re[21]: Покритикуйте архитектуру обмена данными для smart кл
Здравствуйте, Alexander Polyakov, Вы писали:
AP>Вы уверены, что после мерджа DataSeet-ов всё корректно обновиться на UI-е на WPF? (речь про нотификацию шла именно в этом контексте, а не просто абстрактная нотификация. Конкретно WPF не упоминался, но механизм доставки данных как раз и не должен быть сильно завязан на технологию UI-я).
Уверен. Даже специально проверил. Отображается. А за "всё" и "корректно" зуб не дам
Здравствуйте, Alexander Polyakov, Вы писали:
AP>да мердж мерджит . Но я задавал вопрос в надежде услышать детали, как он это делает.
Reflector в руки и бог в помощь! И советую запастись терпением ))) Есть догадка что код мерджа датасетов занимает около 1/4 всего кода фреймворка (шутка). Предположительно мердж сопоставляет записи по ключевым полям. Дальше дело техники.
>>Какие такие флажки в аргументах? AP>Это я задавал вопрос . Я имел ввиду PreserveChanges http://msdn.microsoft.com/en-us/library/aszytsd8.aspx
Понял. Но там же все написано. Что от меня требуется?
>>В случае, когда Product возвращается с сервера одним экземпляром — то обойдется только подменой ссылки, но если Product связан с чем-то еще — мержить графы будет не просто. AP>Предлагаю иногда спускаться с абстрактных вершин и приводить конкретные примеры для подтверждения своих утверждений. Например, если Product связан с ProductCategory, то все так же просто, просто меняется ссылка.
С допущением что в графе объектов могут быть избыточные дублирующие объекты — действительно так же все просто.
>>Вам решать, естественно ли это. Может в Вашем случае нужно чтобы пользователь одновременно видел несколько версий продуктов? AP>Какая-то детсадовская отговорка, типа у вас больше знаний о проект, и только вы можете выбрать правильное решение. У вас разве с опытом не накапливаются экспертные знания, с помощью которых, задав пару тройку вопросов, вы уже можете предложить правильное решение?
Да, я все еще пытаюсь. Экспертные знания — это конечно круто, но я стараюсь делать так, чтобы перед пользователем, хотя бы в пределах одной формы не было противоречивых данных, и чтобы программист не гадал, который экземпляр ProductCategory правильный, а который нет (даже если они только для чтения).
AP>Тк я ж про это и говорил, объекты под Lookup-оп обычно только для чтения, поэтому несколько экземпляров таких объектов не вызывают проблем.
Ну хорошо. Lookpup не вызывает проблем. Где-то кроме Lookup-а они могут быть?
Лично меня напрягает уже то, что здесь приходится оперировать данными, прочитанными в разных транзакциях. Это не важно при чтении справочных данных, которые забиваются один раз и меняются только в дауне. Как читать все остальное — решать надо по месту.
Мэй би я параноик...
AP>При работе с DataSet-ами у меня всегда возникала мысль о том, что они навязывают способ мышления , т.е. ты идешь уже не от того, что надо пользователю, а от того что тебе предлагают DataSet-ы. И ты постоянно занимаешься тем, чтобы всунуть требования пользователя в датасетный подход.
Полной свободы не даст ни один подход. Всегда придется идти а компромиссы.
Re: Покритикуйте архитектуру обмена данными для smart клиент
AP>Теперь о том, как данные будут приходить обратно от клиента к серверу. На клиенте есть некий ClientContext. После десериализации объектов на клиенте, для каждого DTO объекта текущий ClientContext подписывается на изменения в DTO объекте. Пользователь редактирует данные, изменения попадают в DTO объекты, а ClientContext регистрирует у себя все изменения. После того, как пользователь поработал с данными, изменения, зарегистрированные в ClientContext, отправляются на сервер. На сервере эти изменения попадают в объекты LINQ to SQL.
Похоже вас несщадно критикуют на месте работы.
Если не секрет — что за организации из раза в раз разрабатывают новые клиент-серверные приложения — вы ISV или это конкретная разработка?
Re[2]: Покритикуйте архитектуру обмена данными для smart кли
Извиняюсь, что долго не заглядывал в эту тему.
>Похоже вас несщадно критикуют на месте работы.
Не понял вашей шутки.
>Если не секрет — что за организации из раза в раз разрабатывают новые клиент-серверные приложения — вы ISV или это конкретная разработка?
Про компанию рассказывать пока не вижу смысла .
>клиент-серверные приложения
В этом треде вроде речь о трехзвенке -- смарт клиенте.
>что за организации из раза в раз разрабатывают новые клиент-серверные приложения
Вы считаете, что уже никто ничего нового не пишет? Да, согласен, что уже много чего написано. Но вот небольшой вопрос. Зачем Microsoft вводит такие новые технологи, как WPF, LINQ и т.д.? не для старых же приложений?