Отображение данных в DataGrid при помощи LINQ и WCF в Silverlight

ОГЛАВЛЕНИЕ

Данная статья рассмотрит осуществление доступа к SQL-данным при помощи создания веб-сервиса (Web Service) и затем, используя LINQ для того, чтобы создать источник данных к которому вы можете осуществить привязку. Элементом управления к которому мы осуществим привязку данных будет DataGrid.

Навыки

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

  • Осуществление соединения к веб-сервису WCF
  • Использование LINQ для осуществления запроса и получения информации, которую может использовать приложение Silverlight
  • Использование элемента управления DataGrid для отображения информации

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

Ознакомление

Для начала создайте проект с названием SQLData, но убедитесь, что вы выбрали опцию Web Application Project (Проект веб-приложения) , так как мы хотим иметь как проект Silverlight , так и проект сервера, где сможем создать веб-сервис WCF (для осуществления соединения к базе данных.

 

Рис. 3-1. Создание проекта веб-приложения (Web Application Project)

Исследование двух проектов

Visual Studio 2008 создаст два проекта в одном.

 

Рис. 3-2. Два проекта в одном

Общий и первый проект названы SQLData. Первый проект является приложением Silverlight.

Второй проект - SQLData_Web, создан в качестве тестового окружения для приложения Silverlight, и он имеет четыре потенциальных точки входа:

  • Default.aspx
  • Silverlight.js
  • SQLDataTestPage.aspx
  • SQLDataTestPage.html

SQLDataTestPage.aspx в частности создан для тестирования элементов управления Silverlight и бегло взглянув можно заметить, что он включает в себя AJAX ScriptManager и элемент управления ASP:Silverlight , источником управления которого является файл .xap , который будет создан проектом Silverlight:

<form id="form1" runat="server" style="height:100%;">
   <asp:ScriptManager ID="ScriptManager1" runat="server"/>
</form> 


Linq to Sql

LINQ является очень мощным дополнение к CSharp и скорее всего станет основной техникой возврата данных для Silverlight и других технологий .NET. Для изучения Linq я рекомендую вам изучить статью ScottGu's , а также существующие книги по данной тематике. В данной статье мы напишем простой запрос LINQ, который будет обработан мною за вас.

Добавление классов Linq

Для того чтобы начать, щелкните правой кнопкой мыши по серверному проекту, выберите пункт добавления нового элемента (Add->New Item), и затем выберите LinqToSql Classes. Обратите внимание на то, что пояснение под окошком гласит о том, что "классы LINQ to SQL преобразованы в реляционные объекты."

 

Рис. 3-3. Добавление шаблона LinqToSql

Когда откроется окно Object Relational Designer, откройте обозреватель сервера Server Explorer (View->Server Explorer) и перейдите к базе данных AdventureWorkLTs (установленной с SQL Server либо загруженной с Micrsoft.com).

 

Рис. 3-4. Переход к базе данных AdventureWorkLT

Установка AdventureWorksLT

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

  1. Щелкните правой кнопкой мыши по Data Connections (Соединения) и выберите Add Connection (Добавить соединение).
  2. В диалоговом окне Data Source (Источник данных) выберите пункт Microsoft Sql Server и нажмите Continue (Далее)
  3. В диалоговом окне Add Connection (Добавить соединение) введите название вашего сервера - чаще всего это будет MachineName\SqlExpress
  4. Вам понадобится установить базу данных AdventureWorksLT, которую вы сможете загрузить с сайта Microsoft Codeplex (http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=16040) - Вам необходима база "SQL2008 AdventureWorks LT dB v2005 x.86.msi".
  5. Возвращаемся к диалоговому окну Add Connection (Добавить соединение), где вам понадобится найти и добавить только что установленную базу данных AdventureWorksLT (в случае, если у вас Vista, она чаще всего расположена в Program Files (x86)\Microsoft SQL Server.... как это показано на изображении )
  6. Нажмите на Test Connection (Тестировать соединение) для того, чтобы удостовериться в том, что было осуществлено соединение к базе данных Adventure Works; если все прошло успешно - нажмите OK, а если нет, то попробуйте еще раз, либо ищите корень проблемы в шагах установки.

 

Рис. 3-5. Осуществление соединения к базе данных Adventure Works

Для того, чтобы отобразились все таблицы, расширьте и перетащите таблицу Customer в рабочее пространство DataClasses1.dbml Designer.

Обратите внимание на то, что это может подразумевать правый щелчок по Data Connections и выбор Add Connection. В диалоговом окне Choose Data Source (Выбор источника данных) вам надо выбрать Microsoft Sql Server, а остальные опции оставьте так, как они есть. Затем нажмите на Continue. Это откроет диалоговое окно Add Connection, где вы введете название вашего сервера, на котором у вас загружена база данных AdventureworksLT (если вы не загружали базу данных, вы можете остановиться тут, загрузить файл из сайта Microsoft и загрузить его на ваш компьютер). Если вы загрузили ее на ваш компьютер, введите localhost и выберите Windows Authentication либо Sql Server Authentication (если вы не уверены - выберите первый вариант).

 

Рис. 3-6. Перенос таблицы в дизайнер (Designer)

После того как вы сделаете это ,вы можете увидеть предупреждение, подобное показанному на изображении 3-7 . Нажмите OK

 

Рис. 3-7. Предупреждение

Преобразование результирующего класса LINQ в сериализуемый класс

В то время как класс LINQ будет сгенерирован соответственно таблице Customer, по умолчанию данный класс не будет считаться сериализуемым, но для того, чтобы использовать его в веб-сервисе нам придется изменить это. Нажмите на область проектирования (Design Surface), чтобы вызвать свойства всего класса и измените режим Serilaization со значения None (Отсутствует) в Unidirectional (Безусловный)

 

Рис. 3-8. Преобразование результирующего класса LINQ в сериализуемый класс


Создание веб-сервиса WCF

Вы создали класс LINQ (хотя не создали запрос) и теперь веб-сервис (и Intellisense) будет знать о классе Customer и его членах. Теперь вы можете создать веб-сервис при помощи Visual Studio 2008.

Щелкните правой кнопкой мыши по проекту и выберите пункт Add New и из шаблонов выберите WCF Service,

 

Рис. 3-9. Создание веб-сервиса WCF

Результатом будет создание трех новых файлов, которые содержат сервисы для вашего веб-сервиса WCF,

 

Рис. 3-10. Веб-сервис WCF, добавленный в ваш проект

Откройте первый файл, IService1.cs, который содержит контракт (contract), созданный Visual Studio 2008.

public interface IService1
{
   [OperationContract]
   void DoWork();

Мы можем заменить данную "пустышку" контракта любым контрактом, который необходимо будет предоставить веб-сервису. Для нашего примера нам небходимо указать то, что наш веб-сервис будет возвращать список объектов Customer при получении строки, которая представляет собой начало фамилии клиента.

Поэтому мы возвращаем значение метода с Void на List<Customer>. Тем не менее, как только вы начнете изменять тип возвращаемого значения, Intellisense сможет помочь вам с выбором, в частности, потому что мы создали данный тип ранее в определении класса LINQ.

 

Рис. 3-11. Intellisense теперь знает о Customer

Мы назовем метод GetCustomersByLastName.

public interface IService1
{
   [OperationContract]
   List<Customer> GetCustomersByLastName(string lastName);

Изменив контракт в интерфейсе вы должны изменить реализацию в файле .cs . Но зачем нам так мучиться? Когда вы доберетесь до файла cs просто нажмите на интерфейс и появится смарт-тег. Oткройте тег и он предложит вам создать скелет реализации!

 

Рис. 3-12. Intellisense может помочь в реализации интерфейса (Interface)

Выбросьте метод DoWork и заполните GetCustomersByLastName запросом LINQ:

public class Service1 : IService1
{
   #region IService1 Members
 
   public List<Customer> GetCustomersByLastName(string lastName)
   {
     DataClasses1DataContext db = new DataClasses1DataContext();
     var matchingCustomers = from cust in db.Customers
                 where cust.LastName.StartsWith(lastName)
                 select cust;
     return matchingCustomers.ToList();
   }
 
   #endregion

(Комментарий region поставлен в то место, где я попросил смарт-тег создать скелет реализации)

Синтаксис LINQ

Давайте разберем данное выражение LINQ. Сначала мы используем новую переменную интерфейса, которая, оказывается, не зависит от типа (она подразумевает тип, но сама по себе без типа!). Мы ей присвоим значение результата LINQ-запроса, который будет объектом типа IEnumerable.

Синтаксис запроса очень похож на часть SQL выражения Select. Другими словами: «Дайте мне соединение к базе данных, о которой я говорил ранее, и назовите данное соединение db. Перейдите в эту базу данных и найдите таблицу с названием Customers и отыщите все записи, где поле LastName начинается с букв, содержащихся в строке lastName. Дайте мне все результирующие записи. Присвойте все те записи к объекту matchingCustomers, который достаточно умен, чтобы знать о том, что А) он должен быть типа IEnumerable и что Б) когда я вызываю ToList() по отношению к нему, то он должен вернуть List<Customer>.».

Будьте внимательны с привязкой (Binding)!

WCF использует wsHttpBinding в качестве стандартной привязки в файле Web.config:

<services>
   <service behaviorConfiguration="SQLData_Web.Service1Behavior"
        name="SQLData_Web.Service1">
      <endpoint address="" binding="wsHttpBinding"
          contract="SQLData_Web.IService1">
        <identity>
           <dns value="localhost"/>
        </identity>
      </endpoint>
   </service>
</services> 

Тем не менее, Silverlight, поддерживает только основную привязку (SOAP 1.1, и т.д.), поэтому вам надо будет соответственно изменить привязку,
<endpoint address="" binding="basicHttpBinding" contract="SQLData_Web.IService1">

Все - у вас есть готовый сервис.


Создание приложения Silverlight

Следующим шагом будет создание приложения Silverlight , которое будет взаимодействовать с веб-сервисом. Для этого, щелкните правой кнопкой мыши по ссылкам в проекте Silverlight и выберите Add Service Reference (добавить ссылку на сервис)

Рис. 3-13. Добавление ссылки на веб-сервис

Когда появится Add Service Reference нажмите на Discover и выберите Services in Solution. Сервис который вы создали будет найден. До нажатия на OK обратите внимание на Service, оперцию , которую вы создали (GetCustoemrByLastName будет найдена

 

Рис. 3-14. Выбор тех операций, которые необходимо добавить (Add)

Нажав на OK вы добавляете сервис в ваш проект. Вы сможете осуществить доступ к веб-сервисе (и его метод) через его ссылку.

 

Рис. 3-15. Сссылка добавлена в ваш проект

Создание XAML

В Page.xaml я создал очень простой пользовательский интерфейс , который состоит из верхней строки для ввода фамилии пользователя и нижней строки для отображения результатов. Для начала, я расположу строки и колонки элемента Grid:

<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
   <Grid.RowDefinitions>
     <RowDefinition Height="10" /> <!--0 Margin-->
     <RowDefinition Height="50" /> <!--1 Prompts-->
     <RowDefinition Height="*" /> <!--2 DataGrid-->
     <RowDefinition Height="10" /> <!--3 Margin-->
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
     <ColumnDefinition Width="10" /> <!--0 Margin-->
     <ColumnDefinition Width="*" /> <!--1 Controls-->
     <ColumnDefinition Width="10" /> <!--2 Margin-->
   </Grid.ColumnDefinitions>
</Grid> 

Обратите внимание на то, что я установил ShowGridLines в значение True во время работы для того чтобы обеспечить себе факт получения ожидаемых результатов, а также посмотрите на то, что третья строка и вторая колонка используют звездочку для определения размера - это означает, что они должны занимать все оставшееся пространство.

Grid имеет небольшие отступы со всех сторон и две строки - верхнюю и нижнюю:

 

Рис. 3-16. Табличная сетка в режиме дизайнера (design mode)

Добавляем элементы управления в верхнюю строку

Я хочу расположить Textblock (для запроса), TextBox (для ввода) и кнопку в верхней строке. Наиболее легким путем будет реализация при помощи StackPanel, и я обрамлю все рамкой для разделения всего от результатов.

<Border BorderBrush="Black" BorderThickness="2" Grid.Row="1" Grid.Column="1"/>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
   <TextBlock Text="Last name to search for: " VerticalAlignment="Bottom"
    FontSize="18" Margin="15,0,0,0" />
   <TextBox x:Name="LastName" Width="250" Height="30" Margin="2,0,0,4"
    VerticalAlignment="Bottom"/>
   <Button x:Name="Search" Width="75" Height="30"
   Margin="20,0,0,4" Content="Search"
   VerticalAlignment="Bottom" Background="Blue" FontWeight="Bold"
   FontSize="14" />
</StackPanel> 

Наконец, перетащите DataGrid из Toolbox на XAML.

<my:DataGrid x:Name="theDataGrid" AlternatingRowBackground="Beige" AutoGenerateColumns="True" Width="700" Height="500" Grid.Row="2" Grid.Column="1" CanUserResizeColumns="True" />

Вы заметите то, что он получил префикс my и то, что объявлено новое пространство имен для поддержки:

xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

Написание обработчика события для кнопки поиска (Search)

Когда пользователь нажимает кнопку поиска нам надо получить текст в TextBox и передать его веб-сервису и получить в результате набор клиентов. Давайте настроим шаблон кода обработчика события в файле page.xaml.cs:

public Page()
{
   InitializeComponent();
   Loaded += new RoutedEventHandler(Page_Loaded);
}
 
void Page_Loaded(object sender, RoutedEventArgs e)
{
   Search.Click += new RoutedEventHandler(Search_Click);
}
 
void Search_Click(object sender, RoutedEventArgs e)
{
   


Асинхронный вызов сервиса

Вызов веб-сервиса из Silverlight можно сделать только асинхронно (в принципе, так и должно быть, поскольку вы работаете в обозревателе и не можете себе позволить блокировку!)

Первым заданием будет получить ссылку на Service1Client . Вы можете исследовать это в обозревателе объектов, чтобы удостовериться в том, что это именно тот объект, который содержит асинхронные методы (Asynchronous), необходимые нам,

 

Рис.  3-17. Клиент веб-сервиса в обозревателе (изображение немного обрезано для экономии места)

Мы присвоим Service1Client локальному объекту webService:

void Search_Click(object sender, RoutedEventArgs e)
{
  ServiceReference1.Service1Client webService =
   new SQLData.ServiceReference1.Service1Client(); 

Далее мы используем webService для настройки обработчика события для метода, который будет вызван тогда, когда вызывается событие GetCustomersByLastNameCompleted:

webService.GetCustomersByLastNameCompleted +=
   new EventHandler<SQLData.ServiceReference1.
   GetCustomersByLastNameCompletedEventArgs>
   (webService_GetCustomersByLastNameCompleted); 

Наконец-то мы выполнили асинхронный вызов

webService.GetCustomersByLastNameAsync(LastName.Text);

Когда сервис завершает действие, то вызывается событие GetCustomersByLastNameCompleted, а также наш метод. Тщательно составленный список клиентов сохранен в e.Result, который мы присвоим свойству ItemSource элемента DataGrid и все привязки будут теперь иметь источник, к которому они могут соединяться.

void webService_GetCustomersByLastNameCompleted(
   object sender,
   SQLData.ServiceReference1.GetCustomersByLastNameCompletedEventArgs e)
{
 
   theDataGrid.ItemsSource = e.Result;

 

Рис. 3-18. Запущенная программа

Готово!

Создание такого приложения, даже с использованием Styles и многого другого для придания более изящного вида, станет минутным делом, как только вы узнаете, как просто это делается!

Скачать исходники примеров 

Источник