Wcf ria services что это
Перейти к содержимому

Wcf ria services что это

  • автор:

Корпоративные шаблоны на основе WCF RIA Services

Два основных объявления на конференциях PDC09 и Mix10 были посвящены выпуску бета-версии и релиз-кандидату Silverlight 4 соответственно. К моменту, когда вы будете читать эту статью, окончательная версия Silverlight 4 будет доступна для скачивания. Наряду с обширной поддержкой печати, она включает поддержку повышенных разрешений, веб-камер, микрофонов, «тостов» (toasts), доступа к буферу обмена и др. Благодаря новому набору всяческих средств Silverlight 4 уже наступает на пятки Adobe AIR как многоплатформенная инфраструктура UI с богатой функциональностью.

Хотя все это очень интересно, я прежде всего являюсь разработчиком бизнес-приложений, и мне особенно нравится простой способ включения бизнес-данных и логики в приложение Silverlight.

Одна из проблем со специализированными бизнес-приложениями Silverlight — подключение к данным. В Silverlight 3 ничто не мешает вам создать свой WCF-сервис (Windows Communication Foundation) и соединяться с ним, но такой вариант оставляет желать много лучшего, особенно если вы вспомните о бесчисленных способах подключения к данным в настольных или ASP.NET-приложениях. Если настольные и веб-приложения могут подключаться к базе данных напрямую через NHibernate, Entity Framework (EF) или чистые конструкции ADO.NET, то приложения Silverlight отделены от моих данных «облаком». Я называю такое отделение пропастью (data chasm).

Преодолеть эту пропасть может показаться обманчиво простым делом. Очевидно, что в какой-то мере это достигнуто в ряде существующих приложений Silverlight, активно работающих с данными. Но то, что поначалу кажется простым, становится все сложнее и сложнее по мере того, как вы сталкиваетесь с очередными проблемами. Как отслеживать изменения через сеть или инкапсулировать бизнес-логику в сущности, которые находятся по обе стороны брандмауэра?

Для решения этих проблем сейчас появляются сторонние инструменты, но Microsoft тоже видит необходимость в том, чтобы предоставить свое решение, поэтому были созданы WCF RIA Services (ранее .NET RIA Services), или RIA Services для краткости. (Полное введение в RIA Services см. в статье «Building a Data-Driven Expense App with Silverlight 3» в майском номере MSDN Magazine за 2009 г. по ссылке msdn.microsoft.com/magazine/dd695920.) Я был в курсе всех наработок с того момента, как меня впервые пригласили к участию в программе тестирования бета-версии; я вносил предложения для группы разработки и учился применению инфраструктуры в своих приложениях.

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

Представляю KharaPOS

Я создал приложение-пример KharaPOS, иллюстрирующее концепции, которые я намерен представить в этой статье. Это приложение кассового терминала (point-of-sale, POS), реализованное на Silverlight 4 с применением RIA Services, Entity Framework и SQL Server 2008. Конечная цель — создать возможность хостинга этого приложения на платформе Windows Azure и SQL Azure, но в поддержке Windows Azure со стороны Microsoft .NET Framework 4 есть маленькая проблема (или просто отсутствие нужной поддержки).

В то же время KharaPOS — хороший пример использования .NET Framework 4 для создания реального приложения. Проект размещен через CodePlex на KharaPOS.codeplex.com. Вы можете посетить этот сайт и скачать исходный код, просмотреть документацию и присоединиться к дискуссии по разработке этого приложения.

Должен отметить, что большую часть архитектуры и функциональности KharaPOS я позаимствовал из книги Питера Соуда (Peter Coad) в соавторстве с Дэвидом Нортом (David North) и Марком Мэйфилдом (Mark Mayfield) «Object Models: Strategies, Patterns, and Applications, Second Edition» (Prentice Hall PTR, 1996). Я сосредоточусь на одной подсистеме этого приложения — управлении каталогом (рис. 1).

Figure 1 The Entity Data Model for Catalog Management

Рис. 1. Entity Data Model для Catalog Management

Корпоративные шаблоны

Проектировочные шаблоны для разработки корпоративных приложений обсуждаются в целом ряде превосходных книг. Одну из таких книг я постоянно использую как справочник:Мартин Фаулер (Martin Fowler) «Patterns of Enterprise Application Architecture» (Addison-Wesley, 2003). Эта книга и дополняющий ее веб-сайт (martinfowler.com/eaaCatalog/) предоставляют полезные шаблоны для создания бизнес-приложений.

Большое количество шаблонов в каталоге Фаулера относятся к представлению данных и манипуляциям с ними; кроме того, что интересно само по себе, они занимают то же место, что и RIA Services. Понимание этого даст вам более четкую картину того, как адаптировать RIA Services под требования разнообразных бизнес-приложения — от простейших до самых сложных. Я рассмотрю следующие шаблоны:

  • формы и элементы управления;
  • сценарий транзакции;
  • модель предметной области;
  • уровень прикладного сервиса.

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

Формы и элементы управления

Шаблон «формы и элементы управления» (forms-and-controls pattern) (или, как я называю его, «формы поверх данных») помещает всю логику в UI. На первый взгляд это кажется скверной идеей. Но для простого ввода данных и просмотра в виде «основные сведения/детали» (master-detail) это самый простой и самый прямой подход к связыванию UI с базой данных. Во многих инфраструктурах есть встроенная поддержка этого шаблона (например, в Ruby on Rails, ASP.NET Dynamic Data и SubSonic), так что у него явно есть свое место, хотя некоторые называют его «антишаблоном». Многие разработчики низводят подход «формы поверх данных» до уровня начального прототипа, но все же у него имеется своя область применения в конечных приложениях.

Независимо от вашего мнения насчет его полезности нельзя отрицать его простоту. Такой подход не называют быстрой разработкой приложений (rapid application development, RAD), так как он весьма утомителен. RAD в Silverlight вносят WCF RIA Services. Используя Entity Framework, RIA Services и Silverlight Designer, на основе «форм поверх данных» можно создать простой редактор таблицы базы данных буквально в пять шагов.

  1. Создайте новое бизнес-приложение Silverlight.
  2. Добавьте в него новую Entity Data Model (EDM) (используйте мастер для импорта базы данных).
  3. Добавьте сервис предметной области (domain service) в веб-приложение (не забудьте сначала скомпилировать его, чтобы обнаружение EDM прошло должным образом), которое ссылается на модель данных.
  4. Используйте секцию источников данных и перетащите сущность, предоставляемую через RIA Services, на поверхность страницы или пользовательского элемента управления в приложении Silverlight (и вновь скомпилируйте его, чтобы оно могло увидеть новый сервис предметной области).
  5. Добавьте кнопку и отделенный код для сохранения изменений на форме в базу данных; для этого достаточно такой строки:
this.categoryDomainDataSource.SubmitChanges(); 

Теперь у вас есть простая сетка с данными, с помощью которой можно напрямую редактировать существующие записи в вашей таблице. Добавив еще несколько элементов, вы сможете создать форму, которая позволит включать в таблицу новые записи.

Хотя этот шаблон неоднократно демонстрировался, чтобы показать преимущества использования RAD с WCF RIA Services, его все равно стоит привести здесь, так как он обеспечивает базовую разработку с применением этой инфраструктуры. Кроме того, как уже упоминалось, это вполне допустимый шаблон в приложениях на основе RIA Services.

Рекомендация Как и в случае динамических данных ASP.NET, шаблон «формы поверх данных» следует применять для несложных UI администрирования (как в редакторе категории продукта в KharaPOS), где логика проста и прямолинейна: добавление, удаление и изменение записей в таблице. Но, как мы сейчас увидим, Silverlight и RIA Services масштабируются до гораздо более сложных приложений.

Шлюз к табличным данным Стандартный подход к службам RIA Services, который я сейчас исследовал, можно также рассмотреть как реализацию шаблона шлюза табличных данных, как показано на стр. 144-151 книги Фаулера. Используя два уровня абстракции (EF над базой данных и сервис предметной области над EF), я создал простой шлюз к таблицам базы данных, в которых применяются операции CRUD (Create, Read, Update, Delete) и которые возвращают DTO-объекты (Data Transfer Objects) со строгой типизацией.

С технической точки зрения, это нельзя назвать настоящим шлюзом к табличным данным из-за двух уровней абстракции. Но если не придираться, он очень сильно напоминает шаблон шлюза к табличным данным. Если честно, было бы логичнее обсудить сопоставление между RIA Services и шаблоном шлюза к табличным данным, так как все остальные шаблоны в списке являются шаблонами интерфейсов данных, но «формы поверх данных» — это главным образом шаблон UI. Однако мне кажется более целесообразным начать с базового сценария и сфокусироваться на UI, а потом двигаться от него к базе данных.

Model-View-ViewModel (MVVM) Хотя создать функциональную форму на основе «форм поверх данных» несложно, кое-какие трения возникнуть могут. Иллюстрацией может послужить рис. 2, где показан XAML для управления категориями.

Рис. 2. XAML для управления категориями

               " Header="Name" Width="100" />   , Path=Data>" SelectedValue="" SelectedValuePath="Id" DisplayMemberPath="Name"/>   "/>   " Header="Short Description" Width="150" /> " Header="Long Description" Width="*" />    

Столбец для родительской категории в сетке данных является полем с раскрывающимся списком (combobox), который использует список существующих категорий, чтобы можно было выбирать родительскую категорию по имени, а не запоминать ее идентификатор. Увы, Silverlight не по вкусу, когда в дерево визуальных элементов дважды загружается один и тот же объект. Поэтому мне пришлось объявить два источника данных предметной области: один — для сетки и другой — для просмотра поля с раскрывающимся списком. Кроме того, отделенный код (code-behind) для управления категориями получился довольно запутанным (рис. 3).

Рис. 3. Отделенный код для управления категориями

private void DomainDataSourceLoaded(object sender, LoadedDataEventArgs e) < if (e.HasError) < MessageBox.Show(e.Error.ToString(), "Load Error", MessageBoxButton.OK); e.MarkErrorAsHandled(); >> private void SaveButtonClick(object sender, RoutedEventArgs e) < CategoryDomainDataSource.SubmitChanges(); >private void CancelButtonClick(object sender, RoutedEventArgs e) < CategoryDomainDataSource.Load(); >void ReloadChanges(object sender, SubmittedChangesEventArgs e)

Я не собираюсь излагать здесь полное пособие по MVVM — см. отличный трактат на эту тему в статье «WPF Apps with the Model-View-ViewModel Design Pattern» в февральском номере «MSDN Magazine» за 2009 г.(msdn.microsoft.com/magazine/dd419663). На рис. 4 показан один из способов применения MVVM в приложении RIA Services.

Рис. 4 Управление категориями через модель View

public CategoryManagementViewModel() < _dataContext = new CatalogContext(); LoadCategories(); >private void LoadCategories() < IsLoading = true; var loadOperation= _dataContext.Load(_dataContext. GetCategoriesQuery()); loadOperation.Completed += FinishedLoading; >protected bool IsLoading < get < return _IsLoading; >set < _IsLoading = value; NotifyPropertyChanged("IsLoading"); >> private void NotifyPropertyChanged(string propertyName) < if (PropertyChanged!=null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); >void FinishedLoading(object sender, EventArgs e) < IsLoading = false; AvailableCategories= new ObservableCollection(_dataContext.Categories); > public ObservableCollectionAvailableCategories < get < return _AvailableCategories; >set < _AvailableCategories = value; NotifyPropertyChanged("AvailableCategories"); >> 

Как видите, ViewModel отвечает за инициализацию контекста предметной области и уведомление UI в тот момент, когда происходит загрузка, а также за обработку запросов от UI на создание новых категорий, сохранение изменений в существующих категориях и повторную загрузку данных от сервиса предметной области. Это обеспечивает четкое разделение между UI и управляющей им логикой. Может показаться, что шаблон MVVM требует больше работы, но его изящество откроется, как только вам понадобится впервые изменить логику для передачи данных в UI. Кроме того, перенос процесса загрузки категорий в ViewModel позволяет значительно расчистить представление (как его XAML, так и отделенный код).

Рекомендация Используйте MVVM, чтобы избежать запутывания UI-кода сложной UI-логикой или, что еще хуже, запутывания вашей модели бизнес-объектов.

Транзакционный сценарий

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

Шаблон транзакционных сценариев (стр. 110–115 в книге Фаулера) несколько облегчает эту ситуацию. Он позволяет отделить прикладную логику, управляющую данными, от UI.

Как определено Фаулером, транзакционный сценарий «организует бизнес-логику по процедурам, причем каждая процедура обрабатывает один запрос от презентационного уровня». Транзакционные сценарии — нечто гораздо большее простых CRUD-операций. По сути, они размещаются перед шлюзом к табличным данным для обработки CRUD-операций. В предельном случае отдельный транзакционный сценарий обрабатывал бы каждое чтение и запись в базу данных. Но мы же разумные люди и всему знаем меру.

Транзакционный сценарий полезен, когда ваше приложение должно координировать взаимодействие между двумя сущностями, например при создании сопоставления между двумя экземплярами разных классов сущностей. Например, в системе управления каталогами я обозначаю продукт, доступный для заказа бизнес-подразделением, созданием записи в каталоге. Запись идентифицирует продукт, бизнес-подразделение, артикул и срок, в течение которого он может быть заказан как внутри предприятия, так и извне. Чтобы упростить формирование записей в каталоге, я создал метод в сервисе предметной области (см. следующий фрагмент кода), который предоставляет транзакционный сценарий для изменения доступности продукта бизнес-подразделению; при этом в UI не требуется напрямую манипулировать записями в каталоге.

Фактически сервис предметной области даже не открывает доступа к записям в каталоге:

public void CatalogProductForBusinessUnit(Product product, int businessUnitId) < var entry = ObjectContext.CreateObject(); entry.BusinessUnitId = businessUnitId; entry.ProductId = product.Id; entry.DateAdded = DateTime.Now; ObjectContext.CatalogEntries.AddObject(entry); ObjectContext.SaveChanges(); > 

Вместо того чтобы предоставлять его как функцию в контексте доменной области на клиентской стороне, RIA Services генерируют функцию в сущности (в данном случае — Product), которая при вызове помещает уведомление об изменении в объект, интерпретируемый на серверной стороне как вызов метода из сервиса предметной области.

Фаулер рекомендует для подхода к реализации транзакционного сценария:

  1. • с помощью объектов команд, которые инкапсулируют операции и которые при необходимости можно обходить;
  2. • на основе единственного класса, хранящего набор транзакционных сценариев.

Здесь я выбрал второй подход, но ничто не мешает вам использовать команды. Отсутствие прямого доступа к записи в каталоге из UI-уровня делает транзакционный сценарий единственным средством создания записей в каталоге. Если вы используете шаблон команд, это правило вводится в действие по соглашению. Если разработчик забудет о существовании какой-либо команды, все кончится там, откуда начиналось, — в фрагментации и дублировании логики.

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

Рекомендация Используйте транзакционный сценарий, если ваша бизнес-логика становится слишком сложной для «форм поверх данных» или если вы хотите выполнять логику на серверной стороне (или и то, и другое).

Бизнес-логика и UI-логика Я уже несколько раз ссылался на UI- и бизнес-логику, и, хотя разница между ними поначалу может показаться незначительной, она важна. UI-логика отвечает за презентационный уровень:что показывать на экране и как (например, элементы, используемые для заполнения поля со списком). С другой стороны, бизнес-логика — это то, что управляет самим приложением (скажем, определяет скидку на покупку через Интернет). Оба вида логики — важные грани приложения, и, если допускается их смешение, появляется другой шаблон — см. статью Брайена Фута (Brian Foote) и Джозефа Йодера (Joseph Yoder) «Big Ball of Mud» (laputan.org/mud).

Передача нескольких сущностей сервису предметной области Вы можете передавать методу собственного сервиса предметной области только одну сущность. Например, метод:

public void CatalogProductForBusinessUnit(Product product, int businessUnitId) 

не будет работать, если вы попытаетесь использовать следующую сигнатуру вместо предыдущей:

public void CatalogProductForBusinessUnit(Product product, BusinessUnit bu) 

RIA Services не станут генерировать клиентский прокси для этой функции, потому что… ну, в общем, таковы правила. Метод сервиса принимает только одну сущность. В большинстве случаев это не создает никаких проблем:если есть сущность, есть и ее ключ, и вы можете неоднократно считывать ее.

Но допустим просто для демонстрации, что считывание сущности — операция дорогостоящая (возможно, она находится по другую сторону веб-сервиса). Тогда можно сообщить сервису предметной области, чтобы он сохранял копию данной сущности, как показано ниже:

public void StoreBusinessUnit(BusinessUnit bu) < HttpContext.Current.Session[bu.GetType().FullName+bu.Id] = bu; >public void CatalogProductForBusinessUnit(Product product, int businessUnitId) < var currentBu = (BusinessUnit)HttpContext.Current. Session[typeof(BusinessUnit).FullName + businessUnitId]; // Use the retrieved BusinessUnit Here. >

Так как сервис предметной области выполняется в ASP.NET, он имеет полный доступ к сеансу и кешу ASP.NET; это полезно, если вам нужно автоматически удалять объект из памяти по истечении определенного времени. Я использую такой прием в одном из своих проектов, где мне приходится получать CRM-данные от нескольких удаленных веб-сервисов и представлять их пользователю в унифицированном UI. Я применяю явный метод, поскольку какие-то данные стоит кешировать, а какие-то — нет.

Модель предметной области

Иногда бизнес-логика становится настолько сложной, что даже транзакционные сценарии не позволяют управлять ею должным образом. Зачастую такая ситуация проявляется тем, что в транзакционном сценарии содержится логика со сложным ветвлением, или тем, что у вас появляется множество транзакционных сценариев для учета различных нюансов в логике. Другой признак — в приложении появляется все больше транзакционных сценариев и их требуется часто обновлять для соответствия быстро меняющимся требованиям бизнеса.

Если вы заметили любой из этих симптомов, пора подумать о полнофункциональной модели предметной области (с. 116-124 в книге Фаулера). У рассмотренных до сих пор шаблонов было одно общее: сущности — нечто большее, чем DTO-объекты, которые не содержат логики (некоторые считают это антишаблоном и называют его Anemic Domain Model). Одно из основных преимуществ объектно-ориентированной разработки — возможность инкапсулировать данные и связанную с ними логику. Полнофункциональная модель предметной области (rich domain model) использует это преимущество и позволяет поместить логику в сущность.

Детали разработки модели предметной области выходят за рамки этой статьи. Отличное описание этой проблематики см. в книге Эрика Эванса (Eric Evans) «Domain-Driven Design: Tackling Complexity in the Heart of Software» (Addison-Wesley, 2004) или в уже упомянутой книге Коуда по объектным моделям. Однако я могу проиллюстрировать, как модель предметной области помогает справляться с некоторыми из проблем.

Допустим, часть клиентов KharaPOS хочет иметь возможность просматривать хронологию продаж определенных линеек продуктов и с учетом рыночной конъюнктуры решать, что делать с данными линейками товаров — расширять (добавлять товары в линейки), сокращать, полностью сворачивать их или оставить неизменными на текущий сезон.

Данные о продажах уже имеются в другой подсистеме KharaPOS, и в данном случае все, что нужно, есть в системе каталогов. Я просто добавлю в нашу модель сущностей данных представление продаж товаров (только для чтения), как показано на рис. 5.

Figure 5 Entity Data Model Updated with Sales Data

Рис. 5. Entity Data Model, обновленный с Sales Data

Теперь остается добавить логику выборки товаров в модель предметной области. Поскольку я выбираю товары для рынка, я помещу логику в класс BusinessUnit (использование частичного класса с расширением shared.cs или shared.vb сообщает RIA Services, что вы хотите, чтобы он мог работать на клиенте). Исходный код показан на рис. 6.

Рис. 6. Логика домена для выбора продуктов для бизнес-подразделения

public partial class BusinessUnit < public void SelectSeasonalProductsForBusinessUnit( DateTime seasonStart, DateTime seasonEnd) < // Get the total sales for the season var totalSales = (from sale in Sales where sale.DateOfSale >seasonStart && sale.DateOfSale < seasonEnd select sale.LineItems.Sum(line =>line.Cost)). Sum(total=>total); // Get the manufacturers for the business unit var manufacturers = Catalogs.Select(c =>c.Product.ManuFacturer). Distinct(new Equality(i => i.Id)); // Group the sales by manufacturer var salesByManufacturer = (from sale in Sales where sale.DateOfSale > seasonStart && sale.DateOfSale < seasonEnd from lineitem in sale.LineItems join manufacturer in manufacturers on lineitem.Product.ManufacturerId equals manuFacturer.Id select new < Manfacturer = manuFacturer, Amount = lineitem.Cost >).GroupBy(i => i.Manfacturer); foreach (var group in salesByManufacturer) < var manufacturer = group.Key; var pct = group.Sum(t =>t.Amount)/totalSales; SelectCatalogItemsBasedOnPercentage(manufacturer, pct); > > private void SelectCatalogItemsBasedOnPercentage( ManuFacturer manufacturer, decimal pct) < // Rest of logic here. >> 

Выполнить автоматический выбор товаров за сезон так же просто, как вызвать новую функцию в BusinessUnit, а потом функцию SubmitChanges в DomainContext. В будущем, если в этой логике будет обнаружена ошибка или потребуется обновление логики, я буду точно знать, где ее искать. Я не только централизовал логику, но и сделал объектную модель более выразительной. Почему это хорошо, объясняется на с. 246 книги Эванса:

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

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

Рекомендация Используйте модель предметной области, когда ваша логика становится сложной и может охватывать сразу несколько сущностей. Инкапсулируйте логику в объект, с которым она имеет наибольшее сродство, и присвойте его операции описательное имя.

Разница между моделью предметной области и транзакционным сценарием в RIA Services Вероятно, вы заметили, что и в транзакционном сценарии, и в модели предметной области вызов адресуется непосредственно сущности. Однако логика в этих двух шаблонах размещается в разных местах. В случае транзакционного сценария вызов функции применительно к сущности просто указывает контексту/сервису предметной области, что соответствующую функцию нужно вызвать в сервисе предметной области при следующей передаче изменений. В случае модели предметной области логика выполняется на клиентской стороне, а при передаче изменения фиксируются.

Хранилище и объекты запросов Сервис предметной области естественным образом реализует шаблон хранилища (repository pattern) (см. с. 322 в книге Фаулера). В WCF RIA Services Code Gallery (code.msdn.microsoft.com/RiaServices) группа RIA Services предлагает отличный пример создания явной реализации этого шаблона поверх DomainContext. Это расширяет возможности тестирования вашего приложения без обращения к уровню сервисов или базе данных. Кроме того, в моем блоге (azurecoding.net/blogs/brownie) есть реализация шаблона объекта запроса (Фаулер, с. 316) поверх хранилища, при котором выполнение запроса на серверной стороне откладывается до тех пор, пока не происходит реальное перечисление.

Уровень прикладных сервисов

Вопрос навскидку: как вы поступаете в том случае, когда нужно использовать полнофункциональную модель предметной области, но раскрывать ее логику на UI-уровне крайне нежелательно? Вот здесь и пригодится шаблон уровня прикладных сервисов (application service layer pattern) (Фаулер, с. 133). Если у вас есть модель предметной области, этот шаблон легко реализовать переносом логики предметной области из shared.cs в отдельный частичный класс и помещением функции в сервис предметной области, который запускает функцию применительно к сущности.

Уровень прикладных сервисов действует как упрощенный фасад вашей модели предметной области, предоставляя операции, но не их детали. Другое преимущество в том, что ваши объекты предметной области смогут принимать внутренние зависимости, не требуя того же от клиентов уровня прикладных сервисов. В некоторых случаях (см. пример на рис. 6) сервис предметной области выдает один простой вызов, адресованный некой сущности. Иногда он может управлять несколькими сущностями, но будьте осторожны:слишком большой объем операций управления отбросит вас к транзакционному сценарию, и преимущества инкапсуляции логики в самой сущности будут утрачены.

Рекомендация Используйте уровень прикладных сервисов для создания простого фасада модели предметной области и исключите зависимость UI-уровня от ваших сущностей.

Бонус: разграниченный контекст

На форумах RIA участники часто спрашивают:«Как распределить очень большую базу данных между сервисами предметных областей, чтобы ею было легче управлять?». А следом такой вопрос:«как обрабатывать сущности, которые должны присутствовать в нескольких сервисах предметных областей?». Поначалу я считал, что в таких вещах вообще нет нужды; сервис предметной области должен выступать в роли уровня сервисов поверх вашей модели предметной области, и единственный сервис предметной области должен служить фасадом для всей вашей предметной области.

Однако при подготовке этой статьи я наткнулся на шаблон разграниченного контекста (bounded-context pattern) (Эванс, с. 336), о котором я читал раньше, но забыл к тому моменту, когда отвечал на перечисленные выше вопросы. Основное достоинство этого шаблона в том, что в любых крупных проектах используется несколько предметных подобластей. Возьмите для примера KharaPOS, где есть одна модель предметной области для каталогов и другая — для продаж.

Разграниченный контекст позволяет мирно сосуществовать этим моделям предметных областей, даже если у них есть общие элементы (вроде Sale, Business Unit, Product и LineItem, которые присутствуют как в подсистеме продаж, так и в каталоге). К сущностям применяются разные правила в зависимости от того, из какой модели предметной области с ними взаимодействуют (Sale и LineItem предназначены только для чтения в каталоге). Суть в том, что операции никогда не выходят за границы своих контекстов. Это облегчает жизнь, так как Silverlight не поддерживает транзакции, охватывающие несколько сервисов предметных областей.

Рекомендация Используйте разграниченные контексты для разбиения крупной системы на логические подсистемы.

«Яма успеха»

В этой статье мы увидели, что RIA Services поддерживают основные корпоративные шаблоны и требуют при этом минимальных усилий с вашей стороны. Редко, когда инфраструктуры оказываются столь простыми в использовании и в то же время достаточно гибкими для поддержки хоть самых элементарных приложений для ввода данных в электронные таблицы, хоть самых сложных бизнес-приложений, да вдобавок не требуют каких-то радикальных усилий при переходе от одного конца спектра к другому. Это настоящая «яма успеха» («Pit of Success»), о которой упоминал Брэд Адамс (Brad Abrams) в одноименной статье в своем блоге (blogs.msdn.com/brada/archive/2003/10/02/50420.aspx).

Майк Браун (Mike Brown) – президент и соучредитель компании KharaSoft Inc. (kharasoft.com), компании, которая специализируется на обучении, развертывании пользовательского ПО и услугах разработки хостинга как ПО. Он известный специалист по технологиям, обладающий более чем 14-летним опытом работы в отрасли, лауреат премии MVP, соучредитель группы пользователей Indy Alt.NET (indyalt.net) и неутомимый фанат команды Bears!

Выражаю благодарность за рецензирование статьи эксперту: Брэд Абрамс

Проблема совместимости при использовании служб RIA версии 1.0 с пакетом обновления 2 (SP2) вместе с Entity Framework 5

Эта статья поможет устранить проблему совместимости, которая возникает при использовании служб Windows Communication Foundation (WCF) Rich Internet Applications (RIA) версии 1.0 с пакетом обновления 2 (SP2) и Entity Framework (EF) 5 вместе в приложении.

Исходная версия продукта: Entity Framework 4.1
Исходный номер базы знаний: 2975356

Симптомы

При совместном использовании служб WCF RIA версии 1.0 с пакетом обновления 2 (SP2) и EF 5 в приложении приложение может столкнуться с непредсказуемым поведением.

Причина

Эта проблема возникает из-за проблем совместимости метаданных между службами RIA в Microsoft Visual Studio и версиями Entity Framework более поздней версии 4.1.

Обходной путь

Чтобы обойти эту проблему, при выборе шаблона бизнес-приложения Silverlight в Microsoft Visual Studio 2010, Visual Studio 2012 или Visual Studio 2013 используйте RIA версии 1.0 с пакетом обновления 2 (SP2) вместе с Entity Framework 4.1, которая поставляется в качестве варианта по умолчанию. Чтобы использовать более поздние версии Entity Framework, необходимо использовать общедоступные пакеты NuGet.

Методы добавления служб RIA в проект Silverlight

Добавить функции служб RIA в проект Microsoft Silverlight можно двумя способами:

  • Добавьте функции служб RIA, включенные в Visual Studio. Для этого метода требуется Entity Framework 4.1 и поддерживается корпорацией Майкрософт.
  • Добавьте функции служб RIA, доступные в виде пакета NuGet. Этот метод рекомендуется использовать при использовании более поздних версий Entity Framework. Однако она не поддерживается корпорацией Майкрософт.

Дополнительная информация

Дополнительные сведения о службах RIA см. в разделе RIA Services is Getting Open-Sourced. Сведения о пакетах NuGet см. в репозитории на NuGet.org.

Заявление об отказе от ответственности за сведения о продуктах сторонних производителей

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

Обратная связь

Были ли сведения на этой странице полезными?

WCF RIA Services SP1 4.1.61829

WCF RIA Services SP1 4.1.61829

WCF RIA Services SP1 is a framework developed by Microsoft Corporation for building rich, data-driven web applications. It provides a set of tools and libraries that developers can use to easily create and manage web services with a focus on delivering a seamless user experience.

With WCF RIA Services SP1, developers can create services that enable their web applications to interact with databases, business logic and other resources on the web. The framework is designed to allow developers to build services in a flexible and extensible manner, while also providing a high level of security and performance.

  • Easy to Use: Developers can use the WCF RIA Services SP1 toolkit to build services that are intuitive and easy to use.
  • Flexible: The framework provides a wide range of customization options that enable developers to tailor their services to meet their specific needs.
  • Secure: WCF RIA Services SP1 offers robust security features that protect user data and ensure that it is transmitted safely over the web.

WCF RIA Services SP1 is an excellent toolkit for developers who want to build powerful and adaptable web services that deliver an exceptional user experience. Whether you are building a small-scale personal project or a large-scale enterprise-level application, this framework can help you achieve your technical goals with ease.

Обзор

WCF RIA Services SP1 это программное обеспечение Shareware в категории (2), разработанная © Microsoft Corporation.

Проверяли обновления 503 раз пользователями нашего клиентского приложения UpdateStar в прошлом месяце.

Последняя версия WCF RIA Services SP1-4.1.61829, выпущенный на 07.11.2018. Первоначально он был добавлен в нашу базу данных на 16.03.2011. Самой распространенной версией является 4.1.61829, который используется в 38% всех установок.

WCF RIA Services SP1 работает на следующих операционных системах: Windows.

Пользователи WCF RIA Services SP1 дал ему Рейтинг 3 из 5 звезд.

Написать обзор для WCF RIA Services SP1!

WCF RIA Services. Получение данных. Часть 2

В первой части мы ознакомились, что за зверь такой WCF RIA Services, и создали приложение, которое можно коротко назвать, как «Hello world». Все было очень просто, красиво, а так же «drag and drop». Однако для создания настоящих, больших и функциональных приложений нужно еще много чего выучить, например какие возможности доступны в клиентской части, и как их использовать. В этом уроке мы копнем немножко глубже в области получения данных на клиентской стороне. Так же Вы узнаете о конвенциях, которые используются для методов, получающих данные, определенные в серверной части, как их настраивать, используя атрибуты. Расскажу о некоторых программных реализациях, которые можно использовать для получения данных на стороне клиента и как использовать данные, которые используют отличные от Entity Framework источники.

Начальной точкой для изучения всего вышесказанного является проект, который был создан в предыдущей, первой части.

Шаг 1: Добавление в доменную службу параметризованного запроса

После того, как мы создали службу домена в предыдущем уроке, она имела такой вид:

public IQueryable GetTasks()

Этот метод возвращает все записи из таблицы Tasks как перечислимый тип IQueryable и не принимает аргументов. Если же возникает необходимость получить на стороне клиента более специфический запрос, то необходимо добавить метод в службу домена, который в свою очередь будет автоматически доступен клиенту. Так же есть возможность возвращать данные типа IEnumerable, но если основной поставщик данных (Entity Framework или LINQ to SQL) возвращает IQueryable, то это нужно будет реализовать отдельно.

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

public IQueryable GetTasksByStartDate( DateTime lowerDateTimeInclusive, DateTime upperDateTimeInclusive) < return this.ObjectContext.Tasks.Where( t =>t.StartDate >= lowerDateTimeInclusive && t.StartDate
Шаг 2: Изменения UI для получения конкретизированных данных.

Для этого необходимо добавить несколько элементов на нашу страницу. Откройте MainPage.xaml. Сместите Grid таким образом, что б над ним было свободное место для добавления элементов. С помощью «Drag and drop» добавьте из Панели инструментов два TextBox и один Button. Дайте имена созданным элементам, соответственно: lowerDate, upperDate, searchButton.

Измените Content кнопки на «Search By Date».

Шаг 3: Получение результата запроса на клиенте, используя DomainContext

Как Вы помните, DomainContext – это автогенерируемый код, который представляет отражение возможностей серверной части приложения. То есть, если сервис домена называется TasksDomainService, то клиентская часть приложения называется TasksDomainContext. Он предоставляет возможность обращаться к серверной части в асинхронном режиме, а так же сохранять изменения, которые были внесены на клиентской стороне.

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

Добавьте на только что созданную кнопку событие «Click». А в обработчик этого события добавьте следующий код:

private void searchButton_Click(object sender, RoutedEventArgs e) < DateTime lowerDateVal; DateTime upperDateVal; GetDates(out lowerDateVal, out upperDateVal); TasksDomainContext context = new TasksDomainContext(); taskDataGrid.ItemsSource = context.Tasks; EntityQueryquery = context.GetTasksByStartDateQuery(lowerDateVal, upperDateVal); LoadOperation loadOp = context.Load(query); > 

Обратить внимание следует на последние 4 строчки кода, так как первые 3 – просто получение введенной даты из соответствующих полей, и выглядит следующим образом:

 private void GetDates(out DateTime lowerDateVal, out DateTime upperDateVal) < lowerDateVal = DateTime.MinValue; upperDateVal = DateTime.MaxValue; if (!string.IsNullOrWhiteSpace(lowerDate.Text)) < lowerDateVal = DateTime.Parse(lowerDate.Text); >if (!string.IsNullOrWhiteSpace(upperDate.Text)) < upperDateVal = DateTime.Parse(upperDate.Text); >> 

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

Контекст домена предоставляет коллекции сущностей, которые в свою очередь включают в себя наборы коллекций, возвращаемых доменом сервиса после отработки запросов. На данном этапе домен сервиса предоставляет только коллекцию данных из таблицы Tasks.

Обратите внимание, что данный код заменяет ItemsSource в DataGrid коллекцией Tasks до того, как производится какой либо запрос на получение данных. Так нужно в силу того, что запросы, отправляемые контекстом домена являются асинхронными, и заменят содержимое коллекции лишь после прихода ответа от сервера. Данная коллекция реализует интерфейс INotifyCollectionChanged и способна вызывать события, направленные на DataGrid (или любой связанный элемент) для обновления его содержимого.

Затем идет получение EntityQuery от контекста с аргументами, которые передаются в соответствующий метод на сервере. То есть тут мы указываем, что бы мы хотели получить, но вызов еще не происходит. И наконец-то происходит получение LoadOperation от контекста, путем вызова метода Load. Вот тут уже происходит непосредственно обращение к серверу в теневом потоке, а когда запрос выполниться на сервере и придет ответ с необходимыми данными — в потоке UI автоматически обновится вся информация на актуальную.

В принципе, это тоже самое, что происходит под покровом DomainDataSource из первого урока.

Шаг 4: Добавление метода, возвращающего единственный объект

Чтоб метод не возвращал коллекцию, его нужного так и объявить. Далее, после компиляции он будет так же доступен и на стороне клиента:

public Task GetTask(int taskId) < return this.ObjectContext.Tasks.FirstOrDefault(t =>t.TaskId == taskId); > 
Конвенция о настройках

Есть такая тенденция в. NET программировании, когда уменьшается количество кода, необходимого явно прописывать для обеспечения нужных настроек, однако приходиться следовать некоторым правилам именования. Возможно Вы этого еще не заметили, бегло просматривая код службы домена, однако там уже это применяется. Это не очень очевидно на методах, которые извлекают данные, так как метод с любым названием будет отрабатывать одинаково хорошо. Однако для операций обновления, удаления и создания нужно придерживаться определенных правил, основываясь на которых WCF RIA Services ищет и исполняет нужные методы, например, такие как UpdateTask, InsertTask, DeleteTask. Возможно указание нескольких вариантов для каждой операции.

И не забывайте про возможности настройки используя атрибуты. Например для стороны осуществляющей запросы, декорировать методы можно атрибутом [Query], что сделает Ваш код более читабельным и легким для понимания. Нужно помнить, что и тип возвращаемого значения тоже многое говорит о назначении метода. Например, метод возвращающий IQueryable, IEnumberable — однозначно отвечает за извлечение данных. Однако преимущество использования атрибута Query еще в том, что он поддерживает еще некоторые дополнительные параметры, такие как максимальное количество возвращаемых результатов, которые ограничит их количество, даже если из БД вернется после запроса больше необходимого.

Пользовательские доменные службы

А что делать, если Вы не хотите использовать Entity Framework? Как вариант можно воспользоваться LINQ to SQL. Я бы советовал использоваться Entity Framework с новыми проектами. И по возможности со старыми.

LINQ to SQL поддерживается в WCF RIA Services через Toolkit.

Однако многие используют другие стратегии доступа к данным, например nHibernate и другие источники данных, такие как Oracle, MySQL и многие другие. Все это можно так же использовать в WCF RIA Services, но нужно определить объекты данных как простые сущности и использовать их далее в коде как сочтете нужным. Такой подход называется POCO (Plain Old CLR Objects) доменных служб.

Для создания такой доменной службы, класс домена службы нужно наследовать напрямую от DomainService, а не от LinqToEntitiesDomainService. В помощнике создания, кода создаете домен сервиса выберите «Пустой класс службы домена» в выпадающем меню «Доступные классы контекста». Затем определяете сущности, возвращаете коллекции IEnumerable этих сущностей, если Ваш источник данных не поддерживает IQueryable и делайте все, что нужно, в методах на основе источника данных.

Далее Вы должны спроектировать Ваш сервис согласно исходным данным и объектам, с которыми Вы работаете. Эти объекты должны обладать свойствами других объектов или наборов встроенных в .NET поддерживаемых типов.

Ключ, которым Вы обрамляете свои сущности, определяет, что они должны иметь ключевые свойства, которые однозначно их идентифицируют. Обычно «int» или «Guid». Указываете это свойство как атрибут [Key].

Ниже приведен код, который показывает, как объявлять POCO домен службы и его сущности:

public class Foo < [Key] public int FooId < get; set; >public string Name < get; set; >> [EnableClientAccess()] public class MyPOCODomainService : DomainService < public IEnumerableGetFoos() < return new List < new Foo < FooId = 42, Name = "Fred" >>; > > 

В дополнении к атрибуту [Key], если у Вас имеются свойства, которые сами являются типами сущностей(связанные сущности или объединения), то необходимо добавить свойства к этой сущности, например, ID, которое устанавливает соответствие ключевых полей. Кроме того, непосредственно у свойства сущности необходимо указать атрибут [Association], которое показывает ID свойства, которое устанавливает связи(отношения) и сообщает, что это и есть внешний ключ. У свойства сущности еще нужно указать атрибут [Include], что б дочерняя сущность так же всегда извлекалась, когда извлекается родительская по средству RIA Services.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *