В процессе выполнения работы необходимо разработать бизнес-приложение Silverlight.
).
(рис 11.1) Страница создания проектаИнструментальная среда Visual Studio 2010 в соответствии с выбранным шаблоном генерирует новое решение, состоящее из двух проектов (рис 11.2).
(рис 11.2) Главная страница проекта EnterpriceBusinessApplicationВ сгенерированном решении имеются два проекта: клиентский Silverlight-проект EnterpriceBusinessApplication и серверный Web-проект EnterpriceBusinessApplication.Web (рис 11.3)
(рис 11.3) Состав решения Enterprice BusinessApplicationПри запуске проекта на экран выводится главная страница приложения (рис 11.4).
(рис 11.4) Главная страница приложенияНа главной странице имеются три гиперссылки: Home – переход на главную страницу, About – переход на страницу с данными о программе, которые должен подготовить разработчик, login – для перехода на окно авторизации пользователя.
Для локализации приложения необходимо отсоединить клиентскую часть – проект ).
(рис 11.5) Выгрузка клиентской части проекта Для редактирования XAML-описания отсоединенного проекта в контекстном меню выберите пункт ).
(рис 11.6) Редактирование проектаВ XAML-описании проекта для элемента SupportedCultures добавьте код для культуры русского языка ru-RU.
<SupportedCultures>en-US,ru-RU</SupportedCultures>
После сохранения изменений перезагрузите проект (рис 11.7).
(рис 11.7) Перезагрузка проектаДля клиентского проекта а). Копию ресурса переименуйте в б).
(рис 11.8) Файл ресурса строк приложенияДвойным щелчком на файле ресурса .
(рис 11.9) Ресурс строк приложения с английскими значениямиДля локализации приложения на русский язык измените значения строк русскими эквивалентами. На последующих этапах разработки приложения потребуются дополнительные строковые ресурсы, которые необходимо будет добавить с файл строковых ресурсов. На рис 11.10 приведен файл ресурсов с русскими значениями строк.
Аналогичную последовательность действий по копированию, переименованию и переводу значений строк необходимо выполнить для файлов ресурсов клиентского приложения ErrorResources.resx и SecurityQuestions.resx, а также файлов RegistrationDataResources.resx и ValidationErrorResources.resx серверного проекта EnterpriceBusinessApplication.Web.csproj.
Для серверной части проекта необходимо внести изменения в файл EnterpriceBusinessApplicationTestPage.aspx, добавив в головной элемент XAML-описания атрибуты UICulture и Culture.
<%@ Page Language="C#" AutoEventWireup="true" UICulture="auto" Culture="auto"%>
(рис 11.10) Ресурс строк приложения с русскими значениями строкСохраните измененный файл ).
(рис 11.11) Установка языка для Internet ExplorerПосле проведенный работ по локализации приложения главная страница будет иметь вид, приведенный на рис 11.12, а страницы с авторизацией и регистрацией пользователя - на рис 11.13, рис 11.14.

(рис 11.13) Главная страница приложения(рис 11.12) Авторизация пользователя
(рис 11.14) Регистрация пользователяСоздание
Создайте службы WCF
(рис 11.15) Добавление Domain Service ClassПри добавлении сервиса WCF
(рис 11.16) Настройка добавления Domain Service ClassВ результате добавления
(рис 11.17) Обозреватель решений проекта. Серверная частьСозданный серверный класс EmployeeDomainService является, в общем случае, потомком класса DomainService, в нашем случае – это потомок класса LinqToEntitiesDomainService<PersonalEnterpriceEntities>, который обеспечивает доступ к данным на стороне сервера.
Класс EmployeeDomainService помечен атрибутом [ EnableClientAccess() ], который обеспечивает доступ к сервису со стороны клиента. Методы GetEmployees() и GetTitles() класса предназначены для загрузки в приложение данных из базы, а методы Insert(), Update() и Delete() – для манипулирования данными.
Класс EmployeeDomainService.metadata.cs содержим метаданные о сущностях модели данных.
Метаданные используются, в частности, для генерации элементов контроля в клиентской части приложения и
[Display(Name = "Фото", Order = 0)]
public byte[ ] Picture { get; set; }
Для свойств класса EmployeeMetadata необходимо указать атрибут [ Display ], который будет использованы в интерфейсных элементах. Для атрибута Titles дополнительно укажем атрибут [ Include ], который предписывает сформировать коллекцию объектов Title в соответствии со свойствами навигации. Код модифицированного класса EmployeeMetadata приведен в приложении А.
При построении сервиса WCF
(рис 11.18) Обозреватель решений проекта. Клиентская частьВ файле EnterpriceBusinessApplication.Web.g.cs, в частности, имеются классы сущностей, аналогичные классам серверной части приложения и класс EmployeeDomainContext, который является наследником класса DomainContext и обеспечивает доступ к данным и методам сервиса со стороны клиента.
Разработайте страницу для
(рис 11.19) Вкладка Источник данныхПеретащите источник данных для таблицы .
(рис 11.20) Конструктор страницы EmployeePageДобавьте на главной странице Home гиперссылку на созданную страницу EmployeePage, указав для контента расширение разметки для ссылки на строку ресурса ApplicationStrings.EmployeePageTitle.
<HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}"
NavigateUri="/EmployeePage" TargetName="ContentFrame"
Content="{Binding Path=ApplicationStrings.EmployeePageTitle,
Source={StaticResource ResourceWrapper}}"/>
При выполнении приложения в списке сотрудников не выводится фотография и должность. Для отображения фотографии необходимо сделать преобразование двоичного файла в объект Image. Такое преобразование выполняется с помощью конвектора.
Для отображения в сетке employeeDataGrid на странице EmployeePage связанных данных из таблиц необходимо при загрузке сущности Employee обеспечить загрузку связанных с ней данных из таблицы Title. Это можно сделать модифицировав метод GetEmployees() класса сервиса данных EmployeeDomainService.
public IQueryable<Employee> GetEmployees()
{
return this.ObjectContext.Employees.Include("Title").OrderBy(e => e.LastName);
}
В методе GetEmployees() используется класс ObjectContext, который предоставляет возможность выполнять запросы и работать с данными сущности как с объектами. Данный класс обеспечивает взаимодействие с данными как с объектами, которые являются
Метод GetEmployees() возвращает экземпляр класса IQueryable, который представляет типизированный запрос по отношению к
В методе GetEmployees() для сущности Employees используется метод Include() для объединение в запросе сущностей Employees и Titles, а также метод OrderBy() для сортировки результатов запроса по фамилии ( e => e.LastName ).
В XAML-описании колонки Должность сетки employeeDataGrid страницы EmployeePage следует изменить путь привязки, чтобы получить данные о должности ( Title1 ) из сущности Title.
<sdk:DataGridTextColumn x:Name="titleColumn"
Binding="{Binding Path=Title.Title1}"
Header="Должность" Width="150" />
Индикация загрузки данных. При загрузке страницы приложения происходит загрузка данных из базы данных, что требует определенного времени. Во время загрузки данных на экране дисплея появляется пустая страница и не понятно работает или нет приложение. Устранение такой неопределенности можно реализовать с помощью класса BusyIndicator, построенного в соответствии с шаблоном проекта. Использование данного класса обеспечивает вывод элемента управления ProgressBar на экран во время выполнения
Объект BusyIndicator – индикатор длительно выполняемой операции является контейнером, в который необходимо поместить объект, куда должны быть выведены данные в результате выполнения операции. Для разрабатываемого приложения в контейнер BusyIndicatorLoadData поместите сетку employeeDataGrid, которая визуализирует данные о сотрудниках.
<local:BusyIndicator x:Name="BusyIndicatorLoadData"
IsBusy="{Binding IsLoadingData, ElementName= employeeDomainDataSource}"
BusyContent="{Binding Path=ApplicationStrings.BusyIndicatorLoadData,
Source={StaticResource ResourceWrapper}}" >
<sdk:DataGrid Name="employeeDataGrid" . . .>
....
</sdk:DataGrid.Columns>
</local:BusyIndicator>
Свойство IsBusy объекта BusyIndicator задает состояние отображения элемента управления ProgressBar. Данное свойство с помощью расширения разметки привязывается к свойству IsLoadingData объекта DomainDataSource – employeeDomainDataSource. Свойство BusyContent определяет строку, которая будет выводиться в элементе управления ProgressBar при его отображении. В расширении разметки для свойства BusyContent используется задание пути ( ApplicationStrings.BusyIndicatorLoadData ) к статическому ресурсу. При загрузке данных на экране будет отображаться элемент управления ProgressBar с текстом "Загрузка данных . . . ", что устранит для пользователя неопределенность состояния системы в процессе формирования данных в кэше приложения.
Организация перелистывания страниц. При разработке дизайна страницы приложения имеются много вариантов визуализации данных. Например, можно реализовать организацию разбиения и перелистывания страниц с использованием объекта DataPager. Добавьте в XAML-описание страницы EmployeePage объект DataPager после описания индикатора загрузки данных BusyIndicatorLoadData.
<sdk:DataPager Height="26" HorizontalAlignment="Left"
VerticalAlignment="Bottom" Margin="25,0,0,35"
Name="dataPager1" Width="200"
Source="{Binding ElementName=employeeDomainDataSource, Path=Data}"
PageSize="5" />
Источником данных ( Source ) для объекта DataPager является объект DomainDataSource – employeeDomainDataSource, а свойство PageSize задает количество строк, выводимых на одной странице.
Группировка по определенному полю. Предположим, что требуется провести группировку по должности сотрудников. Для этого необходимо в XAML-описание объекта DomainDataSource добавить свойство GroupDescriptors, указав для атрибута PropertyPath, который определяет свойство группировки, значение поля, по которому будет проводиться группировка – Title.Title1.
<riaControls:DomainDataSource AutoLoad="True"
d:DesignData="{d:DesignInstance my:Employee, CreateList=true}"
Height="0" Width="0"
LoadedData="employeeDomainDataSource_LoadedData"
Name="employeeDomainDataSource"
QueryName="GetEmployeesQuery" >
<riaControls:DomainDataSource.DomainContext>
<my1:EmployeeDomainContext />
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.GroupDescriptors>
<riaControls:GroupDescriptor PropertyPath="Title.Title1"/>
</riaControls:DomainDataSource.GroupDescriptors>
</riaControls:DomainDataSource>
Поиск/фильтрация данных. Вначале модифицируем макет страницы EmployeePage. Добавим в сетку LayoutRoot две строки и три столбца.
Контейнер BusyIndicatorLoadData поместим во второй строке, сетки LayoutRoot, объединив для этого три столбца.
<local:BusyIndicator x:Name="BusyIndicatorLoadData" Grid.Row="1" Grid.ColumnSpan="3" ...>
В первой строке и колонки LayoutRoot разметите объект TextBlock с текстом "Список сотрудников".
Во второй колонке первой строки LayoutRoot поместите элементы управления необходимые для задания параметров фильтрации данных: текстовые блоки с текстами "Поиск", "По фамилии" и "По должности", блок для ввода фамилии textBoxSurname и элемент контроля AutoCompleteBox для ввода должности. Все перечисленные элементы контроля скомпонованы в сетке gridSearch, которая помещена в рамку borderSearch. XAML-описание фрагмента страницы Сотрудники сетки gridSearch, визуализирующей элементы контроля для поиска данных по сотрудникам, приведены в приложении А.
Для фильтрации данных по фамилии сотрудника в XAML-описание объекта DomainDataSource необходимо добавить объект FilterDescriptors, который представляет описание фильтра для операций запроса в DomainDataSource объекте.
<riaControls:DomainDataSource.FilterDescriptors>
<riaControls:FilterDescriptor Operator="StartsWith"
PropertyPath="LastName"
Value="{Binding ElementName= textBoxLastName, Path=Text}" />
</riaControls:DomainDataSource.FilterDescriptors>
Свойство Operator задает операцию фильтрации, которая определяется элементами перечисления FilterOperator, а свойство PropertyPath определяет атрибут фильтрации.
В созданном фильтре для фамилии сотрудника использован оперетор StartsWith, который обеспечивает фильтрацию по первым буквам фамилии сотрудника.
При разработки процесса фильтрации по должности рассмотрим, как можно добавлять новые источники данных.
В папку DomainModel добавим класс TitleEmployee для формирования данных о должности сотрудников, имеющихся в сущности Employee.
public class TitleEmployee
{
[Key]
public string Title { set; get; }
}
Свойство Title снабжено атрибутом [ Key ] для обеспечения возможности формирования полей новой сущность на базе этого класса.
В класс сервиса данных EmployeeDomainService добавим метод GetTitlesEmployee() для получения списка должностей сотрудников.
public IQueryable<TitleEmployee> GetTitlesEmployee()
{
return this.ObjectContext.Employees.Select(e => new TitleEmployee
{ Title = e.Title.Title1 }).Distinct();
}
После проведенных изменений в серверной части проекта необходимо модифицировать клиентскую часть приложения. Добавим в XAML-описание страницы EmployeePage новый источник данных, предоставляющий доступ клиента к списку должностей сотрудников.
<riaControls:DomainDataSource AutoLoad="True"
d:DesignData="{d:DesignInstance my1:TitleEmployee, CreateList=true}" Height="0"
LoadedData="TitleEmployeeDomainDataSource_LoadedData" Name="titleEmployeeDomainDataSource"
QueryName="GetTitlesEmployeeQuery" Width="0">
<riaControls:DomainDataSource.DomainContext>
<my1:EmployeeDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Источник данных titleEmployeeDomainDataSource формирует список должностей с помощью метода GetTitlesEmployeeQuery() домена сервиса данных.
Для ввода должности при фильтрации формы используется элемент управления AutoCompleteBox, источником привязки которого служит объект данных titleEmployeeDomainDataSource, значение для данного элемента определяются полем Title.
<input:AutoCompleteBox Grid.Row="2" Grid.Column="1" Name="FilterText"
HorizontalAlignment="Left"
ValueMemberBinding="{Binding Title}"
ItemsSource="{Binding ElementName=titleEmployeeDomainDataSource,
Path=Data}"
TextChanged ="FilterText_TextChanged"
Margin="20,2,20,2" Height="23" Width="300"/>
Элемент управления AutoCompleteBox поддерживает формирование подсказки при вводе первых символов слова, если оно имеется в источнике данных.
В заключении процесса проектирования функции фильтрации данных по должности необходимо в источнике данных employeeDomainDataSource добавить в объект FilterDescriptors XAML-описание фильтра.
<riaControls:FilterDescriptor PropertyPath="Title.Title1"
Operator="IsEqualTo" Value="{Binding ElementName=FilterText, Path=Text}" />
Если в источнике данных указаны несколько объектов FilterDescriptor, то они могут работать по схеме "И" ( Or ) и по схеме "ИЛИ" ( And ). Режим объединения фильтров задается свойством FilterOperator источника данных и в проектируемом приложении ему задано значение Or.
Редактирование данных. На странице приложения создадим кнопки buttonNew, buttonEdit и buttonDelete для реализации выполнения соответствующих операций.
Добавьте в клиентскую часть приложения дочернее окно, с помощью которого будем редактировать и добавлять данные. Для этого в помощнике ) и задать имя ).
(рис 11.21) Создание дочернего окнаВ конструкторе окна ). Для
(рис 11.22) Источники данныхС помощью мыши перетащите сущность ).
(рис 11.23) Дочернее окно EmployeeChildWindowВ XAML-описание окна будет добавлен контекст данных для сущности Employee.
<riaControls:DomainDataSource AutoLoad="True"
d:DesignData="{d:DesignInstance my1:Employee, CreateList=true}"
Name="employeeDomainDataSource" Height="0" Width="0"
LoadedData="employeeDomainDataSource_LoadedData"
QueryName="GetEmployeesQuery">
<riaControls:DomainDataSource.DomainContext>
<my:EmployeeDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Теперь следует создать вызов дочернего окна из страницы EmployeePage при нажатии кнопки buttonEdit.
private void buttonEdit_Click(object sender, RoutedEventArgs e)
{
Employee employeeEdit = employeeDataGrid.SelectedItem as Employee;
if (employeeEdit != null)
{
EmployeeChildWindow winEmpEdit = new EmployeeChildWindow(employeeEdit);
winEmpEdit.Closed += new EventHandler(winEmpEdit_Closed);
winEmpEdit.Title = "Редактирование данных";
winEmpEdit.Show();
}
}
В обработчике нажатия кнопки buttonEdit создается экземпляр employeeEdit сущности Employee на основе выделенной в текущий момент строки сетки данных employeeDataGrid.
Employee employeeEdit = employeeDataGrid.SelectedItem as Employee;
Если объект employeeEdit существует, то создается экземпляр winEmpEdit дочернего окна EmployeeChildWindow, которому в качестве параметра передается
EmployeeChildWindow winEmpEdit = new EmployeeChildWindow(employeeEdit);
Затем включают прослушивания события закрытия окна.
winEmpEdit.Closed += new EventHandler(winEmpEdit_Closed);
Для дочернего формируется заголовок и окно выводится на экран дисплея.
winEmpEdit.Title = "Редактирование данных"; winEmpEdit.Show();
Обработка события закрытия дочернего окна реализуется методом winEmpEdit_Closed(), в котором по результату нажатия кнопки ОК проводят сохранение изменений в базе данных
employeeDomainDataSource.SubmitChanges();
а при нажатии кнопки Отмена – отмена проведенного редактирования данных
employeeDomainDataSource.RejectChanges();
Полный код обработчика winEmpEdit_Closed приводится ниже.
void winEmpEdit_Closed(object sender, EventArgs e)
{
EmployeeChildWindow emp = (EmployeeChildWindow)sender;
if (emp.Employee != null)
{
if (emp.DialogResult == true)
{
employeeDomainDataSource.SubmitChanges();
}
else
{
employeeDomainDataSource.RejectChanges();
}
}
}
При редактировании данных необходимо в списке выделить строку с данными, которые подлежат редактированию, нажать кнопку Редактирование для открытия Окна редактирования.
Для завершения редактирования нажимается кнопка ОК, а для отмены редактирования – кнопка Отмена.
Добавление данных в базу данных. Формирование новых данных можно выполнять с использованием созданного ранее дочернего окна EditEmployeeChildWindow. Для того, чтобы в коде можно было различать режимы в котором находится дочернее окно будем использовать свойство Tag окна. Так при формировании окна в режиме редактирования свойству Tag присвоим значение edit, а при добавлении нового сотрудника – new.
private void buttonNew_Click(object sender, RoutedEventArgs e)
{
Employee newEmployee= new Employee();
newEmployee.Role= "guest";
newEmployee.LastName= "не задано";
newEmployee.FirstName = "не задано";
newEmployee.SecondName = "не задано";
newEmployee.Email = "a@my.com";
newEmployee.Phone = "(999) 999-9999";
newEmployee.Inn = "999999999999";
newEmployee.TitleID = 1;
newEmployee.FirstDate = DateTime.Now;
EmployeeChildWindow winEmpEdit = new
EmployeeChildWindow(newEmployee);
winEmpEdit.Title = "Регистрация нового сотрудника";
winEmpEdit.Tag = "new";
winEmpEdit.Closed += new EventHandler(winEmpEdit_Closed);
winEmpEdit.Show();
}
В методе buttonNew_Click() создается экземпляр класса Employee, задаются его поля, затем по аналогии с режимом редактирования данных, создается экземпляр дочернего окна EmployeeChildWindow, задаются его свойства, включается прослушивание события закрытия окна и оно выводится на экран.
Обработчик события закрытия окна изменен для использования его и для редактирования, и для создания новой записи.
void winEmpEdit_Closed(object sender, EventArgs e)
{
EmployeeChildWindow emp = (EmployeeChildWindow)sender;
switch (emp.Tag.ToString())
{
case "edit":
if (emp.Employee != null)
{
if (emp.DialogResult == true)
{
employeeDomainDataSource.SubmitChanges();
}
else
{
employeeDomainDataSource.RejectChanges();
}
}
break;
case "new":
if (emp.Employee != null)
{
if (emp.DialogResult == true)
{
EmployeeDomainContext employeeDomain =
(employeeDomainDataSource.DomainContext) as EmployeeDomainContext;
employeeDomain.Employees.Add(emp.Employee);
employeeDomainDataSource.SubmitChanges();
}
}
break;
}
}
При создании новой записи по сотруднику используется метод Add сущности контекста домена службы DomainService и изменения в источнике данных фиксируются методом SubmitChanges().
Удаление данных из базы данных.
private void buttonDelete_Click(object sender, RoutedEventArgs e)
{
Employee emp = (Employee)(employeeDataGrid.SelectedItem);
if (emp != null)
{
MessageBoxResult result = MessageBox.Show("Удалить данные",
"Предупреждение", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
EmployeeDomainContext employeeDomain =
(employeeDomainDataSource.DomainContext) as
EmployeeDomainContext;
employeeDomain.Employees.Remove(emp);
employeeDomainDataSource.SubmitChanges();
}
}
}
Удаление данных из источника реализуется методом Remove(), которому в качестве параметра задается удаляемый объект.
Для
Фрагмент кода класса EmployeeMetadata в части описания свойства FirstName приведен ниже.
[Display(Name = "Имя", Order = 2)]
[RegularExpression((@"^[А-Яа-я' ']{1,20}$"),
ErrorMessage = "Недопустимые символы или более 20 символов")]
[Required(ErrorMessage = "Поле обязательно должно быть заполнено")]
public string FirstName { get; set; }
Аналогичные атрибуты установим в классе EmployeeMetadata для свойств LastName, SecondName и для свойства Role, за исключением атрибута [ Required ].
Для свойства Email с помощью регулярного выражения будем проверять требуемый формат для электронной почты, а сообщение об ошибке должно формироваться ссылкой на ресурс.
[Display(Name = "Эл. почта", Order = 7)]
[RegularExpression("^([\\w-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|" +
" (([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$",
ErrorMessageResourceName = "ValidationErrorInvalidEmail",
ErrorMessageResourceType = typeof(ValidationErrorResources))]
public string Email { get; set; }
Для свойства Inn определим допустимость ввода только цифровых символов, которых может быть 12.
[Display(Name = "ИНН", Order = 6)]
[RegularExpression((@"^[0-9]{1,12}$"),
ErrorMessage = "Допустимы толька цифровые символы, длина ИНН - 12 символов")]
[CustomValidation(typeof(ValidaterInn), "ValidateInn")]
public string Inn { get; set; }
Для свойства Inn применен атрибут CustomValidation, который позволяет задать пользовательское правило проверки. В параметрах атрибута CustomValidation определен класс ValidaterInn, определяющий правило проверки, и метод проверки – ValidateInn. Класс ValidaterInn создадим в папке Models/ Shared проекта EnterpriceBusinessApplication.Web.
public class ValidaterInn
{
public static ValidationResult ValidateInn(string Inn)
{
ValidationResult res;
if (Inn.Length == 12)
{
res = ValidationResult.Success;
}
else
{
res = new ValidationResult("ИНН должен содержать 12 символов");
}
return res;
}
}
Статический метод ValidateInn возвращает экземпляр класса ValidationResult, который является контейнером результатов запроса на проверку данных. Если проверка проведена успешно, то возвращается поле ValidationResult.Success, определяющее успешное завершение проверки, в противном случае, создается новый экземпляр класса ValidationResult, которому в конструкторе передается строка, с информацией об ошибке. Класс ValidaterInn создан в проекте EnterpriceBusinessApplication.Web и для того, чтобы он был доступен на клиенте приложения его нужно сделать общедоступным, для чего необходимо переименовать файл класса, добавив к имени .shared, то есть ValidaterInn.shared.cs.
Windows-авторизация. Для настройки Windows- авторизации в проекте EnterpriceBusinessApplication.Web для файла Web.config необходимо свойству mode объекта authentication установить значение Windows.
<authentication mode="Windows"/ >
В клиентском проекте EnterpriceBusinessApplication в конструкторе класса App для свойства аутентификации Authentication веб конекста webContext присвоить значение вновь созданного экземпляра класса Windows аутентификации – WindowsAuthentication.
public App()
{
InitializeComponent();
WebContext webContext = new WebContext();
webContext.Authentication = new WindowsAuthentication();
this.ApplicationLifetimeObjects.Add(webContext);
}
После загрузки приложения происходит автоматически Windows-авторизация пользователя (рис 11.24).
(рис 11.24) Windows-авторизацияПользовательская авторизация. Для аутентификации зарегистрированных в приложении пользователей в проекте EnterpriceBusinessApplication.Web для файла Web.config необходимо свойству mode объекта authentication установить значение Forms и задать имя формы.
<authentication mode="Forms">
<forms name=".EnterpriceBusinessApplication_ASPXAUTH" />
</authentication>
В конструкторе клиентского класса App следует для свойства аутентификации Authentication веб контекста webContext создать экземпляр класса пользовательской авторизации FormsAuthentication.
public App()
{
InitializeComponent();
WebContext webContext = new WebContext();
webContext.Authentication = new FormsAuthentication();
this.ApplicationLifetimeObjects.Add(webContext);
}
Нового пользователя при открытии приложения необходимо зарегистрировать (рис 11.25). Зарегистрированный пользователь должен пройти аутентификацию (рис 11.26). Если аутентификация прошла успешно, то на странице приложения выводится имя пользователя, работающего с приложением (рис 11.27).

(рис 11.26) Регистрация нового пользователя(рис 11.25) Аутентификация пользователя
(рис 11.27) Вход в приложение зарегистрированного пользователяЕсли имя пользователя или пароль введены с ошибкой, то аутентификация пользователя не проходит (рис 11.28).
(рис 11.28) Отказ в аутентификации пользователяРазграничение прав пользователей в Silverlight бизнес-приложении реализуется с использованием ролей. Пользователям приложения назначаются роли, с которыми ассоциируются определенные права или функции системы.
Аутентификация осуществляется на этапе регистрации пользователя, а авторизация реализуется на базе аутентификации с применением механизма ролей.
Для создания ролей необходимо настроить конфигурацию ASP.NET приложения. Для проекта ).
(рис 11.29) Выбор настройки конфигурации ASP.NETНа странице администрирования веб-узла (рис 11.30) следует открыть вкладку ).

(рис 11.31) Страница администрирования веб-узла(рис 11.30) Вкладка безопасность страницы администрирования веб-узлаВыбрав ссылку "Создание ролей и управления ими" перейдем на страницу, где создадим две новые роли: администратор и оператор (рис 11.32).
(рис 11.32) Создание ролей приложенияДля создания новых пользователей выберем ссылку "Создать пользователя" и создадим два новых пользователя – одного с ролью Администратор, а другого – "Оператор" (рис 11.33).
(рис 11.33) Создание нового пользователя с заданием ролиНа странице администрирования веб-узла предоставляются возможности создания и управления пользователями, ролями и правилами доступа.
Для разграничения прав доступа различных пользователей к приложению используются атрибуты для классов и методов службы домена. Если ставится задача ограничить доступ к таблице Employee только зарегистрированным пользователям, метод загрузки данных GetEmployees() для этой таблицы класса EmployeeDomainService должен быть отмечен атрибутом RequiresAuthentication().
[RequiresAuthentication()] public IQueryable<Employee> GetEmployees()
Если требуется разграничить роли пользователей при выполнении определенных операций, то следует применять атрибут RequiresRole ("Имя роли"). Так если оператору, как зарегистрированному пользователю, разрешено только просматривать список сотрудников, а операции модификации, удалении и создания новых данных по сотрудника разрешается выполнять пользователю с ролью администратор, соответствующие методы должны быть отмечены атрибутами RequiresRole (" администратор ").
[RequiresRole("администратор")]
public void InsertEmployee(Employee employee)
{ . . . }
[RequiresRole("администратор")]
public void UpdateEmployee(Employee currentEmployee)
{ . . . }
[RequiresRole("администратор")]
public void DeleteEmployee(Employee employee)
{ . . . }
В результате проведенных изменений в классе EmployeeDomainService операции вставки, обновления и добавления данных по сотрудникам будут доступны только пользователю с ролью администратора, а для пользователя с ролью оператора при попытке выполнения данных операций будет генерироваться исключение.
В процессе выполнения работы необходимо разработать бизнес-приложение Silverlight.
).
(рис 11.1) Страница создания проектаИнструментальная среда Visual Studio 2010 в соответствии с выбранным шаблоном генерирует новое решение, состоящее из двух проектов (рис 11.2).
(рис 11.2) Главная страница проекта EnterpriceBusinessApplicationВ сгенерированном решении имеются два проекта: клиентский Silverlight-проект EnterpriceBusinessApplication и серверный Web-проект EnterpriceBusinessApplication.Web (рис 11.3)
(рис 11.3) Состав решения Enterprice BusinessApplicationПри запуске проекта на экран выводится главная страница приложения (рис 11.4).
(рис 11.4) Главная страница приложенияНа главной странице имеются три гиперссылки: Home – переход на главную страницу, About – переход на страницу с данными о программе, которые должен подготовить разработчик, login – для перехода на окно авторизации пользователя.
Для локализации приложения необходимо отсоединить клиентскую часть – проект ).
(рис 11.5) Выгрузка клиентской части проектаДля редактирования XAML-описания отсоединенного проекта в контекстном меню выберите пункт ).
(рис 11.6) Редактирование проектаВ XAML-описании проекта для элемента SupportedCultures добавьте код для культуры русского языка ru-RU.
<SupportedCultures>en-US,ru-RU</SupportedCultures>
После сохранения изменений перезагрузите проект (рис 11.7).
(рис 11.7) Перезагрузка проектаДля клиентского проекта а). Копию ресурса переименуйте в б).
(рис 11.8) Файл ресурса строк приложенияДвойным щелчком на файле ресурса .
(рис 11.9) Ресурс строк приложения с английскими значениямиДля локализации приложения на русский язык измените значения строк русскими эквивалентами. На последующих этапах разработки приложения потребуются дополнительные строковые ресурсы, которые необходимо будет добавить с файл строковых ресурсов. На рис 11.10 приведен файл ресурсов с русскими значениями строк.
Аналогичную последовательность действий по копированию, переименованию и переводу значений строк необходимо выполнить для файлов ресурсов клиентского приложения ErrorResources.resx и SecurityQuestions.resx, а также файлов RegistrationDataResources.resx и ValidationErrorResources.resx серверного проекта EnterpriceBusinessApplication.Web.csproj.
Для серверной части проекта необходимо внести изменения в файл EnterpriceBusinessApplicationTestPage.aspx, добавив в головной элемент XAML-описания атрибуты UICulture и Culture.
<%@ Page Language="C#" AutoEventWireup="true" UICulture="auto" Culture="auto"%>
(рис 11.10) Ресурс строк приложения с русскими значениями строкСохраните измененный файл ).
(рис 11.11) Установка языка для Internet ExplorerПосле проведенный работ по локализации приложения главная страница будет иметь вид, приведенный на рис 11.12, а страницы с авторизацией и регистрацией пользователя - на рис 11.13, рис 11.14.

(рис 11.13) Главная страница приложения(рис 11.12) Авторизация пользователя
(рис 11.14) Регистрация пользователяСоздание
Создайте службы WCF
(рис 11.15) Добавление Domain Service ClassПри добавлении сервиса WCF
(рис 11.16) Настройка добавления Domain Service ClassВ результате добавления
(рис 11.17) Обозреватель решений проекта. Серверная частьСозданный серверный класс EmployeeDomainService является, в общем случае, потомком класса DomainService, в нашем случае – это потомок класса LinqToEntitiesDomainService<PersonalEnterpriceEntities>, который обеспечивает доступ к данным на стороне сервера.
Класс EmployeeDomainService помечен атрибутом [ EnableClientAccess() ], который обеспечивает доступ к сервису со стороны клиента. Методы GetEmployees() и GetTitles() класса предназначены для загрузки в приложение данных из базы, а методы Insert(), Update() и Delete() – для манипулирования данными.
Класс EmployeeDomainService.metadata.cs содержим метаданные о сущностях модели данных.
Метаданные используются, в частности, для генерации элементов контроля в клиентской части приложения и
[Display(Name = "Фото", Order = 0)]
public byte[ ] Picture { get; set; }
Для свойств класса EmployeeMetadata необходимо указать атрибут [ Display ], который будет использованы в интерфейсных элементах. Для атрибута Titles дополнительно укажем атрибут [ Include ], который предписывает сформировать коллекцию объектов Title в соответствии со свойствами навигации. Код модифицированного класса EmployeeMetadata приведен в приложении А.
При построении сервиса WCF
(рис 11.18) Обозреватель решений проекта. Клиентская частьВ файле EnterpriceBusinessApplication.Web.g.cs, в частности, имеются классы сущностей, аналогичные классам серверной части приложения и класс EmployeeDomainContext, который является наследником класса DomainContext и обеспечивает доступ к данным и методам сервиса со стороны клиента.
Разработайте страницу для
(рис 11.19) Вкладка Источник данныхПеретащите источник данных для таблицы .
(рис 11.20) Конструктор страницы EmployeePageДобавьте на главной странице Home гиперссылку на созданную страницу EmployeePage, указав для контента расширение разметки для ссылки на строку ресурса ApplicationStrings.EmployeePageTitle.
<HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}"
NavigateUri="/EmployeePage" TargetName="ContentFrame"
Content="{Binding Path=ApplicationStrings.EmployeePageTitle,
Source={StaticResource ResourceWrapper}}"/>
При выполнении приложения в списке сотрудников не выводится фотография и должность. Для отображения фотографии необходимо сделать преобразование двоичного файла в объект Image. Такое преобразование выполняется с помощью конвектора.
Для отображения в сетке employeeDataGrid на странице EmployeePage связанных данных из таблиц необходимо при загрузке сущности Employee обеспечить загрузку связанных с ней данных из таблицы Title. Это можно сделать модифицировав метод GetEmployees() класса сервиса данных EmployeeDomainService.
public IQueryable<Employee> GetEmployees()
{
return this.ObjectContext.Employees.Include("Title").OrderBy(e => e.LastName);
}
В методе GetEmployees() используется класс ObjectContext, который предоставляет возможность выполнять запросы и работать с данными сущности как с объектами. Данный класс обеспечивает взаимодействие с данными как с объектами, которые являются
Метод GetEmployees() возвращает экземпляр класса IQueryable, который представляет типизированный запрос по отношению к
В методе GetEmployees() для сущности Employees используется метод Include() для объединение в запросе сущностей Employees и Titles, а также метод OrderBy() для сортировки результатов запроса по фамилии ( e => e.LastName ).
В XAML-описании колонки Должность сетки employeeDataGrid страницы EmployeePage следует изменить путь привязки, чтобы получить данные о должности ( Title1 ) из сущности Title.
<sdk:DataGridTextColumn x:Name="titleColumn"
Binding="{Binding Path=Title.Title1}"
Header="Должность" Width="150" />
Индикация загрузки данных. При загрузке страницы приложения происходит загрузка данных из базы данных, что требует определенного времени. Во время загрузки данных на экране дисплея появляется пустая страница и не понятно работает или нет приложение. Устранение такой неопределенности можно реализовать с помощью класса BusyIndicator, построенного в соответствии с шаблоном проекта. Использование данного класса обеспечивает вывод элемента управления ProgressBar на экран во время выполнения
Объект BusyIndicator – индикатор длительно выполняемой операции является контейнером, в который необходимо поместить объект, куда должны быть выведены данные в результате выполнения операции. Для разрабатываемого приложения в контейнер BusyIndicatorLoadData поместите сетку employeeDataGrid, которая визуализирует данные о сотрудниках.
<local:BusyIndicator x:Name="BusyIndicatorLoadData"
IsBusy="{Binding IsLoadingData, ElementName= employeeDomainDataSource}"
BusyContent="{Binding Path=ApplicationStrings.BusyIndicatorLoadData,
Source={StaticResource ResourceWrapper}}" >
<sdk:DataGrid Name="employeeDataGrid" . . .>
....
</sdk:DataGrid.Columns>
</local:BusyIndicator>
Свойство IsBusy объекта BusyIndicator задает состояние отображения элемента управления ProgressBar. Данное свойство с помощью расширения разметки привязывается к свойству IsLoadingData объекта DomainDataSource – employeeDomainDataSource. Свойство BusyContent определяет строку, которая будет выводиться в элементе управления ProgressBar при его отображении. В расширении разметки для свойства BusyContent используется задание пути ( ApplicationStrings.BusyIndicatorLoadData ) к статическому ресурсу. При загрузке данных на экране будет отображаться элемент управления ProgressBar с текстом "Загрузка данных . . . ", что устранит для пользователя неопределенность состояния системы в процессе формирования данных в кэше приложения.
Организация перелистывания страниц. При разработке дизайна страницы приложения имеются много вариантов визуализации данных. Например, можно реализовать организацию разбиения и перелистывания страниц с использованием объекта DataPager. Добавьте в XAML-описание страницы EmployeePage объект DataPager после описания индикатора загрузки данных BusyIndicatorLoadData.
<sdk:DataPager Height="26" HorizontalAlignment="Left"
VerticalAlignment="Bottom" Margin="25,0,0,35"
Name="dataPager1" Width="200"
Source="{Binding ElementName=employeeDomainDataSource, Path=Data}"
PageSize="5" />
Источником данных ( Source ) для объекта DataPager является объект DomainDataSource – employeeDomainDataSource, а свойство PageSize задает количество строк, выводимых на одной странице.
Группировка по определенному полю. Предположим, что требуется провести группировку по должности сотрудников. Для этого необходимо в XAML-описание объекта DomainDataSource добавить свойство GroupDescriptors, указав для атрибута PropertyPath, который определяет свойство группировки, значение поля, по которому будет проводиться группировка – Title.Title1.
<riaControls:DomainDataSource AutoLoad="True"
d:DesignData="{d:DesignInstance my:Employee, CreateList=true}"
Height="0" Width="0"
LoadedData="employeeDomainDataSource_LoadedData"
Name="employeeDomainDataSource"
QueryName="GetEmployeesQuery" >
<riaControls:DomainDataSource.DomainContext>
<my1:EmployeeDomainContext />
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.GroupDescriptors>
<riaControls:GroupDescriptor PropertyPath="Title.Title1"/>
</riaControls:DomainDataSource.GroupDescriptors>
</riaControls:DomainDataSource>
Поиск/фильтрация данных. Вначале модифицируем макет страницы EmployeePage. Добавим в сетку LayoutRoot две строки и три столбца.
Контейнер BusyIndicatorLoadData поместим во второй строке, сетки LayoutRoot, объединив для этого три столбца.
<local:BusyIndicator x:Name="BusyIndicatorLoadData" Grid.Row="1" Grid.ColumnSpan="3" ...>
В первой строке и колонки LayoutRoot разметите объект TextBlock с текстом "Список сотрудников".
Во второй колонке первой строки LayoutRoot поместите элементы управления необходимые для задания параметров фильтрации данных: текстовые блоки с текстами "Поиск", "По фамилии" и "По должности", блок для ввода фамилии textBoxSurname и элемент контроля AutoCompleteBox для ввода должности. Все перечисленные элементы контроля скомпонованы в сетке gridSearch, которая помещена в рамку borderSearch. XAML-описание фрагмента страницы Сотрудники сетки gridSearch, визуализирующей элементы контроля для поиска данных по сотрудникам, приведены в приложении А.
Для фильтрации данных по фамилии сотрудника в XAML-описание объекта DomainDataSource необходимо добавить объект FilterDescriptors, который представляет описание фильтра для операций запроса в DomainDataSource объекте.
<riaControls:DomainDataSource.FilterDescriptors>
<riaControls:FilterDescriptor Operator="StartsWith"
PropertyPath="LastName"
Value="{Binding ElementName= textBoxLastName, Path=Text}" />
</riaControls:DomainDataSource.FilterDescriptors>
Свойство Operator задает операцию фильтрации, которая определяется элементами перечисления FilterOperator, а свойство PropertyPath определяет атрибут фильтрации.
В созданном фильтре для фамилии сотрудника использован оперетор StartsWith, который обеспечивает фильтрацию по первым буквам фамилии сотрудника.
При разработки процесса фильтрации по должности рассмотрим, как можно добавлять новые источники данных.
В папку DomainModel добавим класс TitleEmployee для формирования данных о должности сотрудников, имеющихся в сущности Employee.
public class TitleEmployee
{
[Key]
public string Title { set; get; }
}
Свойство Title снабжено атрибутом [ Key ] для обеспечения возможности формирования полей новой сущность на базе этого класса.
В класс сервиса данных EmployeeDomainService добавим метод GetTitlesEmployee() для получения списка должностей сотрудников.
public IQueryable<TitleEmployee> GetTitlesEmployee()
{
return this.ObjectContext.Employees.Select(e => new TitleEmployee
{ Title = e.Title.Title1 }).Distinct();
}
После проведенных изменений в серверной части проекта необходимо модифицировать клиентскую часть приложения. Добавим в XAML-описание страницы EmployeePage новый источник данных, предоставляющий доступ клиента к списку должностей сотрудников.
<riaControls:DomainDataSource AutoLoad="True"
d:DesignData="{d:DesignInstance my1:TitleEmployee, CreateList=true}" Height="0"
LoadedData="TitleEmployeeDomainDataSource_LoadedData" Name="titleEmployeeDomainDataSource"
QueryName="GetTitlesEmployeeQuery" Width="0">
<riaControls:DomainDataSource.DomainContext>
<my1:EmployeeDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Источник данных titleEmployeeDomainDataSource формирует список должностей с помощью метода GetTitlesEmployeeQuery() домена сервиса данных.
Для ввода должности при фильтрации формы используется элемент управления AutoCompleteBox, источником привязки которого служит объект данных titleEmployeeDomainDataSource, значение для данного элемента определяются полем Title.
<input:AutoCompleteBox Grid.Row="2" Grid.Column="1" Name="FilterText"
HorizontalAlignment="Left"
ValueMemberBinding="{Binding Title}"
ItemsSource="{Binding ElementName=titleEmployeeDomainDataSource,
Path=Data}"
TextChanged ="FilterText_TextChanged"
Margin="20,2,20,2" Height="23" Width="300"/>
Элемент управления AutoCompleteBox поддерживает формирование подсказки при вводе первых символов слова, если оно имеется в источнике данных.
В заключении процесса проектирования функции фильтрации данных по должности необходимо в источнике данных employeeDomainDataSource добавить в объект FilterDescriptors XAML-описание фильтра.
<riaControls:FilterDescriptor PropertyPath="Title.Title1"
Operator="IsEqualTo" Value="{Binding ElementName=FilterText, Path=Text}" />
Если в источнике данных указаны несколько объектов FilterDescriptor, то они могут работать по схеме "И" ( Or ) и по схеме "ИЛИ" ( And ). Режим объединения фильтров задается свойством FilterOperator источника данных и в проектируемом приложении ему задано значение Or.
Редактирование данных. На странице приложения создадим кнопки buttonNew, buttonEdit и buttonDelete для реализации выполнения соответствующих операций.
Добавьте в клиентскую часть приложения дочернее окно, с помощью которого будем редактировать и добавлять данные. Для этого в помощнике ) и задать имя ).
(рис 11.21) Создание дочернего окнаВ конструкторе окна ). Для
(рис 11.22) Источники данныхС помощью мыши перетащите сущность ).
(рис 11.23) Дочернее окно EmployeeChildWindowВ XAML-описание окна будет добавлен контекст данных для сущности Employee.
<riaControls:DomainDataSource AutoLoad="True"
d:DesignData="{d:DesignInstance my1:Employee, CreateList=true}"
Name="employeeDomainDataSource" Height="0" Width="0"
LoadedData="employeeDomainDataSource_LoadedData"
QueryName="GetEmployeesQuery">
<riaControls:DomainDataSource.DomainContext>
<my:EmployeeDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Теперь следует создать вызов дочернего окна из страницы EmployeePage при нажатии кнопки buttonEdit.
private void buttonEdit_Click(object sender, RoutedEventArgs e)
{
Employee employeeEdit = employeeDataGrid.SelectedItem as Employee;
if (employeeEdit != null)
{
EmployeeChildWindow winEmpEdit = new EmployeeChildWindow(employeeEdit);
winEmpEdit.Closed += new EventHandler(winEmpEdit_Closed);
winEmpEdit.Title = "Редактирование данных";
winEmpEdit.Show();
}
}
В обработчике нажатия кнопки buttonEdit создается экземпляр employeeEdit сущности Employee на основе выделенной в текущий момент строки сетки данных employeeDataGrid.
Employee employeeEdit = employeeDataGrid.SelectedItem as Employee;
Если объект employeeEdit существует, то создается экземпляр winEmpEdit дочернего окна EmployeeChildWindow, которому в качестве параметра передается
EmployeeChildWindow winEmpEdit = new EmployeeChildWindow(employeeEdit);
Затем включают прослушивания события закрытия окна.
winEmpEdit.Closed += new EventHandler(winEmpEdit_Closed);
Для дочернего формируется заголовок и окно выводится на экран дисплея.
winEmpEdit.Title = "Редактирование данных"; winEmpEdit.Show();
Обработка события закрытия дочернего окна реализуется методом winEmpEdit_Closed(), в котором по результату нажатия кнопки ОК проводят сохранение изменений в базе данных
employeeDomainDataSource.SubmitChanges();
а при нажатии кнопки Отмена – отмена проведенного редактирования данных
employeeDomainDataSource.RejectChanges();
Полный код обработчика winEmpEdit_Closed приводится ниже.
void winEmpEdit_Closed(object sender, EventArgs e)
{
EmployeeChildWindow emp = (EmployeeChildWindow)sender;
if (emp.Employee != null)
{
if (emp.DialogResult == true)
{
employeeDomainDataSource.SubmitChanges();
}
else
{
employeeDomainDataSource.RejectChanges();
}
}
}
При редактировании данных необходимо в списке выделить строку с данными, которые подлежат редактированию, нажать кнопку Редактирование для открытия Окна редактирования.
Для завершения редактирования нажимается кнопка ОК, а для отмены редактирования – кнопка Отмена.
Добавление данных в базу данных. Формирование новых данных можно выполнять с использованием созданного ранее дочернего окна EditEmployeeChildWindow. Для того, чтобы в коде можно было различать режимы в котором находится дочернее окно будем использовать свойство Tag окна. Так при формировании окна в режиме редактирования свойству Tag присвоим значение edit, а при добавлении нового сотрудника – new.
private void buttonNew_Click(object sender, RoutedEventArgs e)
{
Employee newEmployee= new Employee();
newEmployee.Role= "guest";
newEmployee.LastName= "не задано";
newEmployee.FirstName = "не задано";
newEmployee.SecondName = "не задано";
newEmployee.Email = "a@my.com";
newEmployee.Phone = "(999) 999-9999";
newEmployee.Inn = "999999999999";
newEmployee.TitleID = 1;
newEmployee.FirstDate = DateTime.Now;
EmployeeChildWindow winEmpEdit = new
EmployeeChildWindow(newEmployee);
winEmpEdit.Title = "Регистрация нового сотрудника";
winEmpEdit.Tag = "new";
winEmpEdit.Closed += new EventHandler(winEmpEdit_Closed);
winEmpEdit.Show();
}
В методе buttonNew_Click() создается экземпляр класса Employee, задаются его поля, затем по аналогии с режимом редактирования данных, создается экземпляр дочернего окна EmployeeChildWindow, задаются его свойства, включается прослушивание события закрытия окна и оно выводится на экран.
Обработчик события закрытия окна изменен для использования его и для редактирования, и для создания новой записи.
void winEmpEdit_Closed(object sender, EventArgs e)
{
EmployeeChildWindow emp = (EmployeeChildWindow)sender;
switch (emp.Tag.ToString())
{
case "edit":
if (emp.Employee != null)
{
if (emp.DialogResult == true)
{
employeeDomainDataSource.SubmitChanges();
}
else
{
employeeDomainDataSource.RejectChanges();
}
}
break;
case "new":
if (emp.Employee != null)
{
if (emp.DialogResult == true)
{
EmployeeDomainContext employeeDomain =
(employeeDomainDataSource.DomainContext) as EmployeeDomainContext;
employeeDomain.Employees.Add(emp.Employee);
employeeDomainDataSource.SubmitChanges();
}
}
break;
}
}
При создании новой записи по сотруднику используется метод Add сущности контекста домена службы DomainService и изменения в источнике данных фиксируются методом SubmitChanges().
Удаление данных из базы данных.
private void buttonDelete_Click(object sender, RoutedEventArgs e)
{
Employee emp = (Employee)(employeeDataGrid.SelectedItem);
if (emp != null)
{
MessageBoxResult result = MessageBox.Show("Удалить данные",
"Предупреждение", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
EmployeeDomainContext employeeDomain =
(employeeDomainDataSource.DomainContext) as
EmployeeDomainContext;
employeeDomain.Employees.Remove(emp);
employeeDomainDataSource.SubmitChanges();
}
}
}
Удаление данных из источника реализуется методом Remove(), которому в качестве параметра задается удаляемый объект.
Для
Фрагмент кода класса EmployeeMetadata в части описания свойства FirstName приведен ниже.
[Display(Name = "Имя", Order = 2)]
[RegularExpression((@"^[А-Яа-я' ']{1,20}$"),
ErrorMessage = "Недопустимые символы или более 20 символов")]
[Required(ErrorMessage = "Поле обязательно должно быть заполнено")]
public string FirstName { get; set; }
Аналогичные атрибуты установим в классе EmployeeMetadata для свойств LastName, SecondName и для свойства Role, за исключением атрибута [ Required ].
Для свойства Email с помощью регулярного выражения будем проверять требуемый формат для электронной почты, а сообщение об ошибке должно формироваться ссылкой на ресурс.
[Display(Name = "Эл. почта", Order = 7)]
[RegularExpression("^([\\w-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|" +
" (([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$",
ErrorMessageResourceName = "ValidationErrorInvalidEmail",
ErrorMessageResourceType = typeof(ValidationErrorResources))]
public string Email { get; set; }
Для свойства Inn определим допустимость ввода только цифровых символов, которых может быть 12.
[Display(Name = "ИНН", Order = 6)]
[RegularExpression((@"^[0-9]{1,12}$"),
ErrorMessage = "Допустимы толька цифровые символы, длина ИНН - 12 символов")]
[CustomValidation(typeof(ValidaterInn), "ValidateInn")]
public string Inn { get; set; }
Для свойства Inn применен атрибут CustomValidation, который позволяет задать пользовательское правило проверки. В параметрах атрибута CustomValidation определен класс ValidaterInn, определяющий правило проверки, и метод проверки – ValidateInn. Класс ValidaterInn создадим в папке Models/ Shared проекта EnterpriceBusinessApplication.Web.
public class ValidaterInn
{
public static ValidationResult ValidateInn(string Inn)
{
ValidationResult res;
if (Inn.Length == 12)
{
res = ValidationResult.Success;
}
else
{
res = new ValidationResult("ИНН должен содержать 12 символов");
}
return res;
}
}
Статический метод ValidateInn возвращает экземпляр класса ValidationResult, который является контейнером результатов запроса на проверку данных. Если проверка проведена успешно, то возвращается поле ValidationResult.Success, определяющее успешное завершение проверки, в противном случае, создается новый экземпляр класса ValidationResult, которому в конструкторе передается строка, с информацией об ошибке. Класс ValidaterInn создан в проекте EnterpriceBusinessApplication.Web и для того, чтобы он был доступен на клиенте приложения его нужно сделать общедоступным, для чего необходимо переименовать файл класса, добавив к имени .shared, то есть ValidaterInn.shared.cs.
Windows-авторизация. Для настройки Windows- авторизации в проекте EnterpriceBusinessApplication.Web для файла Web.config необходимо свойству mode объекта authentication установить значение Windows.
<authentication mode="Windows"/ >
В клиентском проекте EnterpriceBusinessApplication в конструкторе класса App для свойства аутентификации Authentication веб конекста webContext присвоить значение вновь созданного экземпляра класса Windows аутентификации – WindowsAuthentication.
public App()
{
InitializeComponent();
WebContext webContext = new WebContext();
webContext.Authentication = new WindowsAuthentication();
this.ApplicationLifetimeObjects.Add(webContext);
}
После загрузки приложения происходит автоматически Windows-авторизация пользователя (рис 11.24).
(рис 11.24) Windows-авторизацияПользовательская авторизация. Для аутентификации зарегистрированных в приложении пользователей в проекте EnterpriceBusinessApplication.Web для файла Web.config необходимо свойству mode объекта authentication установить значение Forms и задать имя формы.
<authentication mode="Forms">
<forms name=".EnterpriceBusinessApplication_ASPXAUTH" />
</authentication>
В конструкторе клиентского класса App следует для свойства аутентификации Authentication веб контекста webContext создать экземпляр класса пользовательской авторизации FormsAuthentication.
public App()
{
InitializeComponent();
WebContext webContext = new WebContext();
webContext.Authentication = new FormsAuthentication();
this.ApplicationLifetimeObjects.Add(webContext);
}
Нового пользователя при открытии приложения необходимо зарегистрировать (рис 11.25). Зарегистрированный пользователь должен пройти аутентификацию (рис 11.26). Если аутентификация прошла успешно, то на странице приложения выводится имя пользователя, работающего с приложением (рис 11.27).

(рис 11.26) Регистрация нового пользователя(рис 11.25) Аутентификация пользователя
(рис 11.27) Вход в приложение зарегистрированного пользователяЕсли имя пользователя или пароль введены с ошибкой, то аутентификация пользователя не проходит (рис 11.28).
(рис 11.28) Отказ в аутентификации пользователяРазграничение прав пользователей в Silverlight бизнес-приложении реализуется с использованием ролей. Пользователям приложения назначаются роли, с которыми ассоциируются определенные права или функции системы.
Аутентификация осуществляется на этапе регистрации пользователя, а авторизация реализуется на базе аутентификации с применением механизма ролей.
Для создания ролей необходимо настроить конфигурацию ASP.NET приложения. Для проекта ).
(рис 11.29) Выбор настройки конфигурации ASP.NETНа странице администрирования веб-узла (рис 11.30) следует открыть вкладку ).

(рис 11.31) Страница администрирования веб-узла(рис 11.30) Вкладка безопасность страницы администрирования веб-узлаВыбрав ссылку "Создание ролей и управления ими" перейдем на страницу, где создадим две новые роли: администратор и оператор (рис 11.32).
(рис 11.32) Создание ролей приложенияДля создания новых пользователей выберем ссылку "Создать пользователя" и создадим два новых пользователя – одного с ролью Администратор, а другого – "Оператор" (рис 11.33).
(рис 11.33) Создание нового пользователя с заданием ролиНа странице администрирования веб-узла предоставляются возможности создания и управления пользователями, ролями и правилами доступа.
Для разграничения прав доступа различных пользователей к приложению используются атрибуты для классов и методов службы домена. Если ставится задача ограничить доступ к таблице Employee только зарегистрированным пользователям, метод загрузки данных GetEmployees() для этой таблицы класса EmployeeDomainService должен быть отмечен атрибутом RequiresAuthentication().
[RequiresAuthentication()] public IQueryable<Employee> GetEmployees()
Если требуется разграничить роли пользователей при выполнении определенных операций, то следует применять атрибут RequiresRole ("Имя роли"). Так если оператору, как зарегистрированному пользователю, разрешено только просматривать список сотрудников, а операции модификации, удалении и создания новых данных по сотрудника разрешается выполнять пользователю с ролью администратор, соответствующие методы должны быть отмечены атрибутами RequiresRole (" администратор ").
[RequiresRole("администратор")]
public void InsertEmployee(Employee employee)
{ . . . }
[RequiresRole("администратор")]
public void UpdateEmployee(Employee currentEmployee)
{ . . . }
[RequiresRole("администратор")]
public void DeleteEmployee(Employee employee)
{ . . . }
В результате проведенных изменений в классе EmployeeDomainService операции вставки, обновления и добавления данных по сотрудникам будут доступны только пользователю с ролью администратора, а для пользователя с ролью оператора при попытке выполнения данных операций будет генерироваться исключение.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.