Опубликован: 08.07.2011 | Уровень: для всех | Доступ: платный
Самостоятельная работа 2:

Разработка Silverlight-приложения

< Лекция 7 || Самостоятельная работа 2: 12345 || Лекция 8 >

Задание 3. Разработать запросы к службе данных– 2 часа.

Сервисы WCF Data Services позволяют выполнять запросы к службе данных из клиентского приложения на основе библиотеки .NET Framework с использованием сформированных клиентских классов службы данных. Клиентская библиотека преобразует запрос, определенный на клиенте как экземпляр класса DataServiceQuery.

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

При выполнении запросов можно явно вызывать метод асинхронного вызова BeginExecute с объектом DataServiceQuery<T> или выполнить запрос LINQ к именованному объекту DataServiceQuery<T>, который получен из контекста DataServiceContext.

Использование метода асинхронного вызова BeginExecute или выполнение запроса LINQ в лабораторной работе необходимо согласовать с преподаватемем.

Использование метода асинхронного вызова BeginExecute

Подход к применению метода асинхронного вызова BeginExecute рассмотрим не примере базы данных Personal. В лабораторной работе необходимо использовать базу данных в соответствии с вариантом задания.

Для загрузки в приложение таблицы Employee базы данных Personal добавьте в коде класса MainPage ссылки на библиотеку Data.Services.Client и созданный сервис данных Personal

using System.Data.Services.Client;
using SilverlightAppPersonal.Personal;

а также контекст сущностей context сервиса модели данных EmployeeEntities и экземпляр коллекции employees класса DataServiceCollection<T>

EmployeeEntities context;
DataServiceCollection<Employee> employees;

После внесенных изменений код класса MainPage будет следующим:

using System.Windows.Controls;
using System.Data.Services.Client;
using SilverlightAppPersonal.Personal;

namespace SilverlightAppPersonal
{
public partial class MainPage : UserControl
    {
        EmployeeEntities context;
        DataServiceCollection<Employee> employees;
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

Класс DataServiceCollection<T> представляет коллекцию динамических сущностей, обеспечивающую выдачу уведомлений при добавлении и удалении из неё элементов или при обновлении списка. Службы данных WCF используют класс DataServiceCollection<T> с целью поддержки привязки извлекаемых из базы данных для элементов управления Silverlight.

При загрузке главной страницы необходимо выполнить следующие подготовительные действия:

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

Добавьте в XAML-код описание события Loaded класса MainPage со ссылкой на обработчик MainPage_Loaded:

Loaded="MainPage_Loaded"

В коде класса MainPage добавим обработчик MainPage_Loaded:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            context = new EmployeeEntities(new Uri(
"WfcDataServicePerson.svc", UriKind.Relative));
            employees = new DataServiceCollection<Employee>();
            employees.LoadCompleted += new 
               EventHandler<LoadCompletedEventArgs>(employees_LoadCompleted);
            ButtonOpen.IsEnabled = true;
        }

Загрузка данных из базы данных в приложение будет проведена по щелчку кнопки "Загрузить". В XAML-описании добавим для кнопки ButtonOpen обработчик для события Click - Open_Click.

<Button Content="Загрузить" Height="25" Name="ButtonOpen" Margin="35,0,0,0" 
        Padding="10,5,10,5" Click="Open_Click" />

В код класса MainPage добавьте метод Open_Click обработчика события Click кнопки ButtonOpen.

private void Open_Click(object sender, RoutedEventArgs e)
  {
    ResetBindingData();
    DataServiceQuery<Employee> queryEmployee = 
                    context.Employees.AddQueryOption("$orderby", "EmployeeSurname");
    try
    {
        queryEmployee.BeginExecute(OnEmployeeQueryComplete, queryEmployee);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Метод ResetBindingData() предназначен для очистка коллекции данных employees, списков сотрудников listBoxEmployees и должностей comboBoxTitle:

private void ResetBindingData()
{
   employees.Clear();
   listBoxEmployees.ItemsSource = null;
   comboBoxTitle.ItemsSource = null;
   LayoutRoot.UpdateLayout();
}

Далее формируется запрос queryEmployee на получение данных, при этом задается режим сортировки данных по фамилии сотрудника ( EmployeeSurname ):

DataServiceQuery<Employee>  queryEmployee = 
            context.Employees.AddQueryOption("$orderby", "EmployeeSurname");

С помощью метода AddQueryOption в запрос добавляются параметры.

Запросы службы данных WCF для Silverlight выполняются асинхронно. Это реализуется методом BeginExecute(), который использует в качестве параметров делегат OnEmployeeQueryComplete и запрос queryEmployee.

queryEmployee.BeginExecute(OnEmployeeQueryComplete, queryEmployee);

Делегат OnEmployeeQueryComplete использует класс Dispatcher для того, чтобы обеспечить асинхронное получение результата в правильном потоке.

private void OnEmployeeQueryComplete(IAsyncResult result)
{
    Dispatcher.BeginInvoke(() =>
    {
        DataServiceQuery<Employee> queryEmployee = 
                             result.AsyncState as DataServiceQuery<Employee>;
        try
        {
            employees.LoadAsync(queryEmployee);
        }
        catch (DataServiceQueryException ex)
        {
            MessageBox.Show(string.Format("Ошибка запроса в БД: {0} - {1}",
            ex.Response.StatusCode.ToString(), ex.Response.Error.Message));
        }
    });
}

Добавьте в код класса MainPage делегат employees_LoadCompleted для события LoadCompleted коллекции employees, в котором после завершения формирования коллекции employees задается источник данных для списка listBoxEmployees и производится выделение первого элемента списка.

private void employees_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        if (employees.Continuation != null)
        {
            employees.LoadNextPartialSetAsync();
        }
        else
        {
            listBoxEmployees.ItemsSource = employees;
            listBoxEmployees.UpdateLayout();
            if (listBoxEmployees.Items.Count > 0)
                listBoxEmployees.SelectedIndex = 0;
            ButtonOpen.IsEnabled = false;
            
        }
    }
    else
    {
        MessageBox.Show(string.Format("Ошибка формирования коллекции: {0}",
                                                         e.Error.Message));
        ButtonOpen.IsEnabled = true;
    }
}

Использование запроса LINQ

Подход к выполнению запрос LINQрассмотрим не примере базы данных Personal. В лабораторной работе необходимо использовать базу данных в соответствии с вариантом задания.

Класс DataServiceQuery реализует интерфейс IQueryable, определяемый языком LINQ. Клиентская библиотека службы WCF Data Services может преобразовывать запросы LINQ к данным набора сущностей в URI, который представляет выражение запроса, вычисляемое для ресурса службы данных.

Сформируйте запрос LINQ для получения приложением данных сущности Employee.

var allEmpoyees = from emp in context.Employees
                  orderby emp.EmployeeSurname
                  select emp;

Формирование коллекции employees класса DataServiceCollection<Employee> для приложения Silverlight может осуществляться только асинхронно, что определяет необходимость использования метода LoadAsync, который требует приведения параметра метода к типу DataServiceQuery<T>, в нашем случае к типу DataServiceQuery<Employee>.

employees.LoadAsync(allEmpoyees as DataServiceQuery<Employee>);

При асинхронной загрузке коллекции employees необходимо сформировать и обработать события загрузки коллекции типа employees.LoadCompleted для сущности Employee и события titles.LoadCompleted для сущности JobTitle.

Для случая использования запроса LINQ метод Open_Click() будет представлен следующим кодом.

private void Open_Click(object sender, RoutedEventArgs e)
{
    ResetBindingData();
    listBoxEmployees.ItemsSource = null;
    comboBoxTitle.ItemsSource = null;
    LayoutRoot.UpdateLayout();
    try
    {
        var allEmpoyees = from emp in context.Employees
                          orderby emp.EmployeeSurname
                          select emp;
        employees.LoadAsync(allEmpoyees as DataServiceQuery<Employee>);
        var allTitle = from titl in context.JobTitles
                       select titl;
        titles.LoadAsync(allTitle as DataServiceQuery<JobTitle>);
    }
    catch (DataServiceClientException ex)
    {
        MessageBox.Show(ex.Message);
    }
}

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

< Лекция 7 || Самостоятельная работа 2: 12345 || Лекция 8 >
Александр Петров
Александр Петров

При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. 
Вопрос, как отследить и отключить добавление элемента в Items?

Максим Спиридонов
Максим Спиридонов

В пятой лекции на второй странице в компиляторе выскакивает ошибка в строчке :

ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;

Ошибка CS0029

Не удается неявно преобразовать тип "System.Data.Entity.DbSet<WpfApplProject.Employee>" в "System.Data.Entity.Core.Objects.ObjectQuery<WpfApplProject.Employee>".

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

Igor Chelyadinski
Igor Chelyadinski
Беларусь, Минск, №54, 2013
Валентина Алешина
Валентина Алешина
Россия