About company Our development services Portfolio. Case studies Support Testimonials Contacts Brutka Ltd: web site development and custom software development

Information technology news archive


Приложения для привязки данных с помощью ADO.NET и настраиваемые объекты

Средства управления привязкой Windows Forms значительно улучшены по сравнению с прежними версиями приложения. Они предоставляют быстродействующие и удобные средства обработки избыточных задач, связанных с формами для настройки, возможности которых можно настроить и значительно расширить в соответствии с нуждами пользователя. Данные, в том числе объекты DataSets и объекты настраиваемого класса, можно перемещать в различные контейнеры, а средства привязки Windows® Forms дают возможность связать все эти типы объектов. Вместо DataSet можно создавать настраиваемые объекты и использовать их в качестве хранилища данных для приложения, а также можно использовать List<T> и другие типы коллекций для хранения набора настраиваемых объектов. Эти типы настраиваемых объектов могут быть легко связаны с помощью BindingSource и BindingNavigator. В этом разделе я покажу, как связать настраиваемый список бизнес-объектов с помощью средств привязки, представленных в Microsoft® .NET Framework 2.0, и я сделаю это, создав полнофункциональное приложение управления данными Windows Forms.
Я начну с описания этого приложения, в частности, с использования средств управления привязкой DataGridView, BindingSource и BindingNavigator. Затем я пройду по нижним уровням и покажу, как их создавать и как извлекать, сохранять и обращаться к данным и отправлять их обратно в базу данных. Весь программный код примера приложения содержится в загружаемом файле данного выпуска.
Проверка работы приложения
С помощью этого приложения пользователь может просматривать, добавлять, удалять, обновлять, искать записи и перемещаться по ним. Данные заказа Northwind загружаются в DataGridView. После того как заказ выбран, TextBoxes, ComboBoxes и другие средства управления в правой части формы заполняются соответствующей информацией. Все средства управления связаны с одним и тем же источником данных посредством элемента управления BindingSource.
Рис. 1 Отображение заказов Northwind в DataGridView (Щелкните изображение, чтобы увеличить его)
Элемент управления BindingNavigator представлен в виде панели инструментов в верхней части формы. Он имеет стандартные кнопки переходов для изменения записи заказа, которая отображается на экране. Кнопки переходов работают во взаимодействии с таблицей в левой части, что позволяет им оставаться синхронизированными с текущей записью. На панели инструментов имеются также кнопки, управляющие обработчиком событий для добавления, удаления и обновления сведений о заказе. Наконец, приложение позволяет выполнять поиск определенного заказа (обратите внимание на значок бинокля).
Поля для записей о заказе, которые являются ссылками на внешние ключи, могут быть отображены с помощью элементов управления ComboBox. Например, ComboBox можно использовать для отображения списка продавцов (то есть сотрудников). Продавца для определенного заказа можно выбрать в ComboBox. Данная функция, очевидно, имеет большую значимость для пользователя приложения, чем отображение идентификатора сотрудника. Обратите внимание в ComboBox отображается имя сотрулдника, а не его идентификатор. В ComboBox также отображается имя клиента.

Реализация настраиваемых объектов и интерфейсов
Наряду с мощным инструментом DataSet в арсенале средств доступа к данным также представляет большие возможности применение настраиваемых классов для управления и отображения в приложении такой модели данных. Мнения "за" и "против" широко обсуждаются, и многие пользователи твердо придерживаются либо лагеря DataSet, либо лагеря настраиваемых классов. В действительности, оба метода жизнеспособны в архитектуре предприятия. Кроме того, средства ADO.NET работают как с DataSets, так и с настраиваемыми классами при создании объектов для представления объектов данных. В конечном счете, вам нужно некое хранилище для размещения в нем данных. В данном приложении я использую настраиваемые объекты.
Пример приложения включает два проекта: один – для представления данных, и другой – для бизнес-логики и доступа к данным. При создании настраиваемого объекта на нижнем уровне вы создаете свойства для этой сущности. Например, класс Customer имеет свойства CustomerID и CompanyName.В то время как написание такого кода может стать утомительным, особенно в сравнении с использованием DataSet, применение некоторых инструментов рефакторинга, которые создают свойства «на лету», или даже инструмента создания кода для формирования целого класса, может сделать создание класса весьма простым.
Класса Customer в этом примере реализует интерфейс InotifyPropertyChanged, который переводит на передний план событие, вызванное свойством PropertyChanged при изменении свойства CompanyName. Обратите внимание, что средство доступа установки свойства CompanyName выполняет проверку того, что значение действительно было изменено, прежде чем установить новое значение. В этом случае событие PropertyChanged переводится на передний план и данный класс может уведомить всех прослушивающих сеть пользователей об этом изменении. В моем приложении при получении уведомления об изменении BindingSource автоматически обновляет значения элементов управления формы.
Данное приложение также содержит три класса объектов: Order, Customer и Employee. Все они реализуют интерфейс INotifyPropertyChanged и содержат средства доступа к параметрам Property, с помощью которых извлекаются и устанавливаются значения для свойств.

Общие классы и ADO.NET
После построения объектов, необходимо создать методы их извлечения и сохранения. Объекты в этом приложении реализуют стандартный список статических методов, включая все или некоторые из следующих: GetEntity, GetEntityList, SaveEntity и DeleteEntity.
Можно создать один класс для объектов и отдельный класс, содержащий методы доступа к данным. Обычно я разделяю эти классы, только если архитектура требует, чтобы объекты не были тесно связаны с теми методами, с помощью которых они сохраняются и извлекаются. В этом примере приложения методы тесно связаны, поэтому я поместил их в одном классе и сделал их статическими.
GetEntityList<Order>, который возвращает список объектов Order, представляющий все заказы в базе данных Northwind. (Разумеется, этот список можно фильтровать, если мы добавим параметр заказов для конкретного клиента или диапазона дат.) При использовании общих классов и возвращающегося списка List<T> вместо ArrayList код гарантирует, что в этом списке будут находится только объекты типа Т. Это означает также, что пользователь имеет доступ к свойствам объекта в пределах List<T> без преобразования типа объекта, что было бы необходимо, если бы объект был сохранен в нестандартном списке. Например, можно получить OrderID из первого заказа в списке с помощью следующего кода:
List<Order> orderList = GetMyListOfOrders();
int orderID = orderList[0].OrderID;
Если объекты хранились в нестандартном списке, то данный объект следовало бы преобразовать в этот тип:
ArrayList orderList = GetMyListOfOrders();
int orderID = ((Order)orderList[0])).OrderID;
Можно было создать заказы и заполнить ими DataSet или DataTable. Однако я использовал SqlDataReader, так как предпочитаю более быстрый доступ к данным, который обеспечивается в этом случае. Я Я загрузил данные через SqlDataReader, задавая значения каждой строки и заполняя объект Order, и затем добавил этот объект в список List<Order>, повторив процесс для каждой строки в SqlDataReader. Можно было также использовать DataTable и повторить цикл через DataRows. Разница в скорости выполнения процедуры незначительна, но в этом случае действительно нет никакого преимущества в использонии дополнительных данных DataTable, так как я просто выполняю итерацию по рядам и заполняю свой список настраиваемых объектов. Метод FillOrder выполняет следующий код, которыя создает экземпляр объекта Order и устанавливает его свойства из SqlDataReader:
Order order = new Order();
order.OrderID = Convert.ToInt32(rdr["OrderID"]);
order.CustomerID = rdr["CustomerID"].ToString();
order.EmployeeID = Convert.ToInt32(rdr["EmployeeID"]);
order.OrderDate = Convert.ToDateTime(rdr["OrderDate"]);
order.ShipVia = rdr["ShipVia"].ToString();
order.ShipName = rdr["ShipName"].ToString();
order.ShipAddress = rdr["ShipAddress"].ToString();
order.ShipCity = rdr["ShipCity"].ToString();
order.ShipCountry = rdr["ShipCountry"].ToString();
return order;
Обратите внимание, что я передал CommandBehavior.CloseConnection методу ExecuteReader. В этом случае объект SqlConnection будет закрыт, как только закроется SqlDataReader.
В объектах есть также статические методы для вставки, обновления и удаления данных. Я создал статический метод для общего пользования под называнием SaveEntity, который принимает объект и определяет, является он вновь созданным или уже существующим, а затем вызывает соответствующую сохраненную процедуру для выполнения запроса действия. Статический метод AddEntity для класса Customer принимает объект Order и затем сопоставляет значения объекта с соответствующими параметрами сохраненной процедуры.
ADO.NET и настраиваемые объекты – важные части этой архитектуры. Методы извлечения используют ADO.NET для получения данных из базы данных, затем настраиваемые объекты заполняются и возвращаются на уровень представления. Методы SaveEntity и DeleteEntity принимают настраиваемые объекты и затем используют их значения для выполнения изменений в базе данных.

Источники данных
Теперь я покажу, как создаются, заполняются и возвращаются настраиваемые объекты. Для этого пройдемся по уровню представления. Поскольку в моем проекте библиотеки классов есть классы Customer, Order и Employee, я могу использовать их для создания связанных элементов управления форме. В окне «Источники данных» представлены все источники данных, которые доступны в проекте.
Источники данных можно получить из веб-службы, объекта или базы данных. В данном случае я добавил источники данных объекта, так как использую объекты класса. Я добавил источники данных с помощью мастера, который предлагает выбрать пространство имен и класс, который нужно добавить в качестве источника данных. Если уже есть ссылка на проект библиотеки классов, то будут отображены классы Customer, Employee и Order.
После добавления источники данных отображаются в окне «Источники данных». Источник данных Order отображает все общие свойства класса Order. Рядом с каждым именем свойства находится значок, представляющий тип элемента управления, который будет использоваться для отображения значения свойства. Свойство OrderDate отображает элемент управления DateTimePicker, а ShipAddress – элемент управления TextBox. Эти элементы управления можно изменить, щелкнув мышью текст имени свойства в окне «Источники данных» и выбрав другой элемент в раскрывающемся списке. Например, я изменил элемент управления для свойства OrderID на элемент Label, поскольку мне нужно, чтобы он был доступен только для чтения.
Мне не нужно, чтобы свойства, которые являются полями ссылок на другие объекты, такие как свойство Order.CustomerID, отображались напрямую. Вместо этого я хочу, чтобы для них отображалось более описательное значение. Например, я изменил тип элемента управления для свойства CustomerID на ComboBox с целью наполнения списка всеми данными клиента и отображения соответствующего свойства CompanyName клиента. Я заменил на ComboBoxes оба свойства CustomerID и EmployeeID. Можно также отменить отображение свойства в форме, выбрав в списке вместо типа элемента управления параметр [None].
После настройки свойств и элементов управления мне нужно задать их отображение. Для этого я щелкнул объект Order в окне «Источники данных» и выбрал в списке параметр Details (Подробно). Это позволяет мне перетащить источник данных Order в форму и автоматически создать в форме элемент управления отображением и элемент управления привязкой (BindingSource и BindingNavigator). Таким образом был создан элемент управления в правой части формы.

Элементы управления привязкой
BindingSource является неотображаемым элементом управления и представляет собой связь между объектом Order и элементами управления формы. Текущий объект Order в BindingSource будет отображаться в элементах управления в правой части экрана, где его можно просматривать или изменять. В данном приложении он будет связан с объектом List<Order>, который я передам из статического метода GetEntityList класса Order.
Элемент управления BindingNavigator был также создан с помощью перетаскивания источника данных Order в форму. Этот элемент управления отображает вверху формы панель инструментов, которая по умолчанию содержит кнопки для выполнения действий по добавлению, сохранению и удалению записей, а также кнопки переходов. BindingNavigator синхронизирован с элементом управления BindingSource, поэтому при переносе записи в другой элемент управления он автоматически отображает изменение позиции. Например, если нажать кнопку «Далее» в элементе управления BindingNavigator, текущий объект элемента управления BindingSource будет заменен следующим объектом, и в свою очередь, элемент управления справа отобразит значения свойств текущего объекта.
Кнопки BindingNavigator также являются элементами управления, поэтому они также обладают свойствами и событиями, которые можно задать. На панели инструментов я добавил обработчиков событий для кнопок «Сохранить» и «Удалить», поэтому могу задать подтверждение и другую логику перед тем, как измененные данные поступят в базу данных с помощью предварительно созданных статических методов доступа к данным объектов.
Хотя элемент управления BindingNavigator выполняет перемещение записи и обеспечивает легкий способ сохранения изменений данных, его включать не обязательно. После того как этот элемент управления был создан в результате перетаскивания источника данных в форму, его можно удалить из формы без всяких последствий. Конечно, затем нужно написать собственный код, с помощью которого будет выполняться перемещение по списку заказов и сохранение изменений. В любом случае, вы действительно зависите от требований к интерфейсу пользователя.
Элемент ComboBox поля CustomerID постоянно связан со свойством CustomerID источника данных Order (посредством элемента управления BindingSource). Мне также нужно заполнить ComboBox списком клиентов и отобразить CompanyName клиента. После создания проекта библиотеки класса, я создал объект Customer и вывел из него метод GetEntityList<Customer>. Затем я добавил источник данных для объекта Customer. Для этого я перетащил источник данных Customer в CustomerID ComboBox. В результате создается второй элемент управления BindingSource. ComboBox использует этот новый элемент BindingSource, чтобы загрузить список клиентов для отображения. Поэтому выбранное значение ComboBox все еще связано с элементом управления BindingSource объекта Order, и список клиентов и их свойства CompanyName отображаются в элементе управления. Этот процесс был также повторен, чтобы заполнить список для Employee ComboBox. Поскольку я перетащил источник данных в ComboBox, нет никакой необходимости в другом элементе управления BindingNavigator.
Однако я должен предоставить пользователям дополнительный способ перемещения по записям заказов помимо панели инструментов BindingNavigator. Для этого я вернулся в окно «Источники данных», щелкнул источник данных Order, выбрал DataGridView и перетащил источник данных Order в форму. Таким образом был создан элемент управления DataGridView формы, которая содержит столбец, представляющий свойства каждого объекта Order. Затем я удалил лишние столбцы, чтобы в таблице отображались только основные сведения. Дополнительные элементы управления привязкой не были созданы, так как в форме уже существует элемент управления BindingSource, который связывает источник данных Order. На данный момент текущий заказ является одним и тем же для всех элементов управления, так как все они связаны с элементом управлением BindingSource объекта Order.

Код привязки
К данному моменту я создал элементы управления, но еще не написал код для формы. Все элементы управления BindingSource для клиентов, сотрудников и заказов установлены и готовы к работе, но я должен еще передать им данные. Это можно легко сделать, сначала вызывая статический метод каждого объекта (GetEntityList) и извлекая список List<T> каждого объекта. List<T> затем преобразуется в BindingList<T> и устанавливается в качестве источника данных для каждого соответствующего элемента управления BindingSource.
Это весь код, необходимый в интерфейсе пользователя, чтобы позволить пользователя перемещаться по данным и просматривать их. Я вызываю метод SetupBindings в конструкторе формы, поэтому данные извлекаются, связываются и отображаются во время первой загрузки формы. Затем пользователь может перемещаться по записям с помощью кнопок переходов на панели инструментов BindingNavigator или, выбрав строку в элементе управления DataGridView. Однако нам еще нужно написать обработчики событий, с помощью которых можно выполнять изменения.

Сохранение данных
Мне нужно приложение, в котором пользователь сможет удалять текущий выбранный и отображенный заказ, нажав кнопку «Удалить» на панели инструментов. Во-первых, я установил для свойства DeleteItem кнопки «Удалить» значение «none», чтобы удаление выполнялось с помощью собственного кода. Затем я добавил обработчик событий, с помощью которого будет выполняться удаление. Он вызывает частный метод под называнием Delete.
Метод Delete извлекает текущий заказ из свойства Current элемента BindingSource и отправляет его типу объекта Order. Затем у пользователя запрашивается подтверждение удаления заказа. Если пользователь нажимает кнопку ОК, объект Order передается статическому методу DeleteEntity из проекта библиотеки классов. Наконец, заказ удаляется из BindingSource посредством выполнения метода RemoveCurrent элемента управления BindingSource.
Панель управления BindingNavigator имеет также кнопку «Добавить», с помощью которой можно добавить строку в DataGridView и очистить элементы управления в правой части формы. Пользователь может ввести и выбрать значения для нового заказа и нажать кнопку «Сохранить» на панели инструментов BindingNavigator. Я добавил обработчик событий для кнопки «Сохранить», с помощью которого объект заказа передается статическому методу SaveEntity объекта Order. Пользователь может также изменить текущую запись заказа и нажать ту же кнопку «Сохранить», чтобы передать измененный объект заказа статическому методу SaveEntity объекта Order. С помощью метода SaveEntity можно выполнять как вставку, так и обновление, выбрав значение свойства OrderID объекта Order. После того как объект был вставлен или обновлен, элемент DataGridView перезагружается, снова извлекая список заказов и переустанавливая источник данных BindingSource, чтобы поддерживать данные обновленными.

Использование предикатов для поиска объекта
Важно создать удобный интерфейс пользователя. Пользователь может сразу перейти к нужному заказу, зная его идентификатор (Order ID). Для этого я добавил элемент управления ToolStripTextBox в BindingNavigator, где пользователь может ввести полностью или частично код OrderID для перехода к заказу. Затем я добавил кнопку к панели инструментов и обработчик событий, который запускает поиск записи заказа.
Поиск DataRow в DataTable проще выполнять с помощью метода Select или Find. Использование настраиваемых объектов не означает, что я должен отказаться от этих функций. В этом случае, я должен искать объект Order в списке List<Order>, начинающемся со значения, которое пользователь вводит в элементе управления поиском. В списке List<T> предоставляется несколько методов для поиска элементов списка, включая методы Find, FindAll, FindIndex, FindLast и FindLastIndex. Я буду использовать метод Find, который допускает предикат Predicate<T> как единственный аргумент.
Предикат Predicate<T> должен быть предикатом Predicate<Order>, так как у меня есть список List<Order>. Предикат является делегатом, который выпол

2007-03-20

 

More IT news, prices and information