Visual Basic, .NET, ASP, VBScript
 

   
 
Описание для автора не найдено
 
     
   
 

Использование True DBgrid при создании приложений для управления базами данных на Visual Studio.Net (часть 4) – использование C1DataObjects – построение многоуровневых приложений.

 

 

Построение многоуровневого приложения с использованием C1DataObjects.

 

При построении многоуровневого приложения задача распадается как минимум на два этапа:

1.      Создание источника данных (data library в терминологии C1);

2.      Создание клиента.

 

Клиент многоуровневого приложения создается точно так же, как и direct client приложение. Однако в таком клиенте нет необходимости устанавливать компонент c1SchemaDef, поскольку теперь он будет расположен в data library. О том, как сделать так, чтобы клиент работал с data library, а не с локальной БД посмотрим позже, когда будет готов сам источник данных.

 

Построение многоуровневого приложения с использованием C1DataObjects – создаем источник данных.

 

Для облегчения труда программиста при написании data library ComponentOne предоставляет соответствующий шаблон.

 

Откроем Visual Studio и выберем опцию создания нового проекта. Если на компьютере установлена ComponentOne Studio.NET, то в списке шаблонов можно увидеть шаблон ComponentOne Data Library:

 

 

 

                                                               Рис1.

 

Здесь показано окно нового проекта C#. Точно такой же шаблон имеется и в окне проекта VB.

 

Да простят меня программисты VB, дальше, во избежание перегрузки текста я буду приводить только код C#. Надеюсь, что в случае необходимости перевести его в код VB не составит особого труда.

 

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

 

 

                                                               Рис 2.

 

В результате будет создан Component Library проект, состоящий из двух классов:

 

 

        Рис 3.

1.                       DataClass – класс, содержащий компоненты доступа к данным и бизнес-логику. Как минимум, этот класс должен содержать компонент C1SchemaDef.

 

2.                       RemoteService – класс, обеспечивающий взаимодействие с C1DataSet на удаленном компьютере.

 

 

Если вы создаете приложение-источник данных вручную (без помощи мастера), не забудьте в файл AssemblyInfo добавить строки:

 

 

[assembly: C1.Data.SchemaClass(typeof(PhoneLibrary.DataClass))]

[assembly: C1.Data.RemoteServiceClass(typeof(PhoneLibrary.RemoteService))]

 

Как видите, здесь указывается на два фундаментальных элемента при построении удаленного взаимодействия с использованием C1 –

1. Класс, инкапсулирующий схему данных;

2. Класс, обеспечивающий удаленное взаимодействие.

Если более внимательно рассмотреть DataClass, то можно увидеть, что он уже содержит компонент C1SchemaDef.

В принципе, теперь достаточно настроить C1SchemaDef (и в случае необходимости бизнес-логику), и мы получим уже вполне работоспособную программу. В результате ее компиляции получим .dll – файл, который можно применять совместно с клиентом.

 

Для использования полученной data library в клиенте, необходимо:

 

1.      установить ссылку на dll – файл

2.      установить свойство Вataibrary компонента C1DataSet клиента

 

 

               Рис 4.

 

 

Построение многоуровневого приложения – создаем логику взаимодействия с удаленным компонентом.

 

Полученное приложение мало чем отличается от  простого direct client приложения. Но каждый программист хорошо знает о проблемах, возникающих, если источник данных и клиент находятся на разных машинах в сети (особенно в режиме «удаленного доступа»). Эти проблемы возрастают в геометрической прогрессии, если необходимо обеспечить взаимодействие нескольких источников данных. Вы наверное знаете транзакции, блокировки, контроль доступа и т. д.

 

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

Этот класс является наследником класса C1.Data.RemoteDataService, который собственно и содержит методы Fill и Update, посредством которых и осуществляется удаленное взаимодействие.

RemoteDataService – абстрактный класс, для работы с ним необходимо создать его класс – наследник (RemoteService) и включить его в data library. Теперь вызовы методов Fill и Update клиентского компонента C1DataSet автоматически будут вызывать методы Fill и Update класса RemoteDataService. Методы Fill и Update класса RemoteDataService являются виртуальными. Нам теперь необходимо переопределить эти методы в классе RemoteService1 и наполнить их логикой.

При использовании компонентов C1.Data в многоуровневом режиме через границы приложений пересылается экземпляр класса C1DataSet (не путать с компонентом C1DataSet !), который является наследником MarshalByRefObject, а следовательно изначально сериализуем.

 

Сигнатура перегруженного метода Fill:

 

public override void Fill (C1DataSet dataSet, FilterConditions filterConditions,

string[] diagEntityNames, C1.Data.SchemaObjects.ConnectionCollection    dynamicConnections, bool beginTransaction)

{

      // Здесь вы можете производить любые свои действия

      // Если нет пользовательского метода заполнения датасета, позволяем           // сделать это базовому методу.

      base.Fill(dataSet, filterConditions, diagEntityNames, dynamicConnections,       beginTransaction)

}

 

Здесь:

 

filterConditionsэкземпляр класса FilterConditions, который представляет собой             коллекцию объектов FilterConditions, определяющих условия отбора и сортировки записей;

            

string[] diagEntityNamesсписок объектов базы данных, из которых необходимо         извлечь информацию;

 

dynamicConnectionsколлекция динамических подключений;

 

beginTransactionбулева переменная, разрешающая или запрещающая      применение             транзакций.

 

Аналогично должен быть перегружен и метод Update:

 

public override void Update (C1DataSet dataSet,       C1.Data.SchemaObjects.ConnectionCollection dynamicConnections, bool       beginTransaction)

{

      // Здесь вы можете производить любые свои действия

      // Если нет пользовательского метода обновления датасета, позволяем           // сделать это базовому методу.

      base.Update(dataSet, dynamicConnections, beginTransaction);

}

 

 

Таким образом, осуществляя вызов из клиента вы можете:

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

- определить условия обновления (заполнения) данных;

- динамически назначить подключение к базе данных.

 

В теле перегруженных методов можно:

- проверять данные на допустимость и отсутствие конфликтов;

- управлять блокировками и транзакциями базы данных;

- генерировать события, оповещающие других клиентов об изменениях.

 

Методы Fill и Update имеют много вариантов с различной сигнатурой. Выше приведены только наиболее полные варианты. Все приведенные параметры вызовов методов являются необязательными. В минимальном варианте вызовы этих методов Fill() и Update().

В зависимости от стоящих перед вами задач вы можете выбрать и перегрузить требуемый вариант.

 

Кроме перегрузки методов Fill и Update никто не мешает вам самостоятельно написать процедуры заполнения датасетов и обновления данных.

 

Пример построения многоуровневого приложения.

 

Давайте откажемся от стандартной базы данных Northwind, и создадим свою. Я создал примитивный телефонный справочник. Его схема:

 

 

                                       Рис 5.

 

Здесь:

- Groups – группы абонентов (покупатели, поставщики, и т. д.);

- CType – типы телефонных номеров (рабочий, домашний, и т. д.);

- Persons – список людей;

- PNum – телефонные номера.

 

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

 

Теперь создаем клиента:

 

 

                                                               Рис 6.

 

Как видите, выбор группы абонентов я сделал в виде комбобокса. Конечно же, теперь его можно напрямую связать его с датасетом, но я хочу, чтобы вместе со списком групп в комбобоксе был пункт «Все группы». Поэтому заполним комбобокс программно.

 

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

 

public class cItem

      {

            private int m_grID;

            private string m_grName;

 

            public cItem(int grID,string grName)

            {

                  m_grID=grID;

                  m_grName=grName;

            }

 

            public int GrID

            {

                  get{return m_grID;}

            }

 

            public override string ToString()

            {

                  return m_grName;

            }

}

 

Устанавливаем свойства c1DataSet1 как показано на Рис 4.

 

Заполнять список нам придется не раз, поэтому пишем отдельную процедуру:

 

private void FillCombo(int rn)

            {

                  // очищаем комбобокс

                  comboBox1.Items.Clear();

                  // добавляем пункт "Все группы"

                  comboBox1.Items.Add(new cItem(0, "Все группы"));

                  // заполняем комбобокс остальными данными

                  C1.Data.C1DataTable t1=c1DataSet1.Tables[0];

                  for(int i=0;i<t1.Rows.Count;i++)

                  {

                        C1.Data.C1DataRow r1=t1.Rows[i];

                        comboBox1.Items.Add(new cItem((int)r1[1], r1[0].ToString()));

                  }

                  // устанавливаем указатель комбобокса на нужную запись

                  try

                  {

                        comboBox1.SelectedIndex=rn;

                  }

                  catch

                  {

                        comboBox1.SelectedIndex=0;

                  }

       }

 

Теперь можно записать в конструкторе формы:

 

FillCombo(0);

 

Запустите приложение и убедитесь, что комбобокс заполняется значениями правильно.

 

Теперь давайте займемся заполнением списка абонентов. Создаем обработчик  выбора значения комбобокса:

 

private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)

            {

                  int gi=((cItem)comboBox1.SelectedItem).GrID;// получаем идентификатор группы

                  this.c1DataSet2.Clear();

                  if(gi==0)// если нужно вывести список всех абонентов

                  {

                        c1DataSet2.Fill();

                  }

                  else // требуется вывести абонентов какой-либо группы

                  {

                        // создаем объект фильтра

                        FilterConditions filterConditions=new FilterConditions();

                        filterConditions.Add(new FilterCondition(null,"Persons","[IDGr]=" + gi.ToString()));

                        // заполняем датасет с использованием фильтра

                        try

                        {

                              c1DataSet2.Fill(filterConditions, false);

                        }

                        catch

                        {

                        }

                  }

       }

 

Как сделать чтобы при перемещении по записям первой таблицы изменялись значения во второй, вы уже знаете. Запускаем приложение:

 

 

                                                               Рис 7.

 

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

 

 

Конфигурирование многоуровневого приложения для работы в сети.

 

Если библиотека данных и клиент находятся на разных компьютерах в сети (не важно, локальной или в Internet) для библиотеки данных необходима программа-сервер, задача которой сконфигурировать Remoting и опубликовать в сети точку доступа к библиотеке. Для работы в сети и к сборкам клиента необходимо добавить средства Remoting, и соответствующим образом сконфигурировать их. Так вот, в библиотеку C1.Data средства Remoting встроены изначально и существенно облегчена настройка клиента. Настройка сервера выполняется так же, как и для стандартного Remoting.

 

Конфигурирование библиотеки данных при использовании хостинга на базе IIS.

Использование IIS является наиболее простым способом размещения библиотеки в сети. Его преимущества:

1.      простота настройки. IIS берет на себя много проблем, которые в противном случае легли бы на плечи программиста:

                    - регистрация канала;

                    - управление временем жизни объектов;

                    - безопасность и доступ.

2.      возможность использования «сверхтонкого» клиента на базе web-браузера;

3.      независимость от платформы клиента.

 

Недостатки:

1.      Возможно использование только протокола http;

2.      Не поддерживается механизм обратных вызовов, что не позволяет делать «событийно-управляемые» приложения.

 

Об использовании, настройке и администрированию IIS написано достаточно много. Поэтому я не буду особо вдаваться в подробности.

Создадим виртуальный каталог:

 

 

                                                               Рис 8.

 

В качестве реального каталога, соответствующего созданному виртуальному, выбираем папку с нашим проектом. Для простоты установим анонимный доступ. Наша сборка PhoneLibrary.dll должна при этом находится либо в подкаталоге bin выбранного каталога, либо в GAC. Далее, в нашу папку необходимо поместить файл Web.config. Ниже приводится пример этого файла:

 

<configuration>

      <system.runtime.remoting>

            <application>

                  <service>

                        <wellknown mode="Singleton"

                        Type="PhoneLibrary.RemoteService,PhoneLibrary"

                        objectUri="Phone.soap"/>

                  </service>

            </application>

      </system.runtimw.remoting>

</configuration>

 

Элемент Type имеет следующую сигнатуру:

 

                        ПространствоИмен, ИмяКласса, ИмяСборки

 

objectUri – идентификатор нашей сборки в сети. Теперь к ней можно обращаться по адресу (например):

 

http://www.mycompany.ru/ Phone.soap

 

Теперь нам осталось внести изменения в клиент. Здесь все не просто, а очень просто:

 

  1. открываем страницу свойств DataSet клиента
  2. устанавливаем свойства DataLibrary и DataLibraryUrl:

 

 

                                                      Рис 9

 

Можно запускать! Если работает сеть, будет работать и наше приложение.

 

Конфигурирование библиотеки данных при использовании хостинга на базе собственного приложения.

 

Данный способ лишен основных недостатков IIS, но имеет свой  недостаток – все надо делать своими руками.

Существует три варианта сервера:

- Windows – приложение;

- консольное приложение;

- служба Windows.

 

Давайте выберем первый способ, как наиболее простой.

 

Добавим к приложению еще один Windows Form проект. Назовем его PhoneServer. Студия создаст заготовку проекта с формой. В нашем приложении форма не несет никакой смысловой нагрузки (хотя на ней можно поместить, например, картинку, или написать что-то вроде: «Эта крутая программа создана мною, любимым»), поэтому сделаем ее изначально невидимой. А чтобы пользователь знал, что программа работает, поместим в трей соответствующую иконку.

Помещаем на форму контролы notifyIcon и contextMenu. В контекстное меню добавляем пункты:

- «Показать форму»;

- «Скрыть форму»;

- «Выход»

 

Назначаем созданное контекстное меню контролу notifyIcon. Остается вручную дописать недостающий код:

 

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

 

namespace PhoneServer

{

     

      public class frmMain : System.Windows.Forms.Form

      {

            private System.Windows.Forms.NotifyIcon notifyIcon1;

            private System.Windows.Forms.ContextMenu contextMenu1;

            private System.Windows.Forms.MenuItem menuItem1;

            private System.Windows.Forms.MenuItem menuItem2;

            private System.Windows.Forms.MenuItem menuItem3;

            private System.ComponentModel.IContainer components;

 

            public frmMain()

            {

                  InitializeComponent();

 

                  ///

                  /// в этом месте будет добавлен код конфигурации Remoting

                  ///

 

                  //при запуске пункт меню "скрыть форму" делаем невидимым

                  contextMenu1.MenuItems[1].Enabled=false;

            }

 

            protected override void Dispose( bool disposing )

            {

                  if( disposing )

                  {

                        if (components != null)

                        {

                              components.Dispose();

                        }

                  }

                  base.Dispose( disposing );

            }

 

            #region Windows Form Designer generated code

            // …..

            #endregion

 

           

            [STAThread]

            static void Main()

            {

                  //приложение запускается с изначально невидимой формой

                  new frmMain();

                  Application.Run();

            }

 

            private void menuItem1_Click(object sender, System.EventArgs e)

            {

                  ShowDialog();

            }

 

            private void menuItem2_Click(object sender, System.EventArgs e)

            {

                  Close();

            }

 

            private void menuItem3_Click(object sender, System.EventArgs e)

            {

                  Application.Exit();

            }

 

            private void frmMain_Closed(object sender, System.EventArgs e)

            {

                  contextMenu1.MenuItems[0].Enabled=true;

                  contextMenu1.MenuItems[1].Enabled=false;

            }

 

            private void frmMain_Load(object sender, System.EventArgs e)

            {

                  contextMenu1.MenuItems[0].Enabled=false;

                  contextMenu1.MenuItems[1].Enabled=true;

            }

      }

}

 

Само по себе такое приложение еще ничего не делает. Его нужно связать с библиотекой PhoneLibrary и сконфигурировать Remoting.

 

1.      Добавляем в проект ссылку на сборку PhoneLibrary.

2.      Добавляем к коду формы ссылки на необходимые нам пространства имен Remoting:

 

                        using System.Runtime.Remoting;

 

3.      Конфигурируем Remoting.

 

Способов конфигурирования Remoting существует два: непосредственно в коде и с помощью конфигурационного файла. Давайте используем второй способ.

 В меню Project выбираем пункт Add New Item… В открывшемся диалоговом окне выбираем Application configuration file. К проекту будет добавлен файл App.config (при компиляции студия автоматически переименует этот файл в формате «Имя_приложения.exe.config» и поместит его в одну директорию с исполняемым файлом). Наполнять содержимым конфигурационный файл придется вручную, например:

 

<configuration>

      <system.runtime.remoting>

            <application name="PhoneServer">

                  <service>

                        <wellknown mode="Singleton"

                              type="PhoneLibrary.RemoteService,PhoneLibrary"

                              objectUri="Phones"/>

                  </service> 

                  <channels>

                        <channel ref="tcp"

                            port="4040" >

                            <serverProviders>

                                    <formatter ref="binary"

                                          typeFilterLevel="Full"/>

                              </serverProviders>

                        </channel>

                  </channels>

            </application>

      </system.runtime.remoting>

</configuration>

 

Теперь остается только добавить в наш код строчку (ее место в приведенном выше коде я обозначил)

 

RemotingConfiguration.Configure("PhoneServer.exe.config");

 

Теперь к нашей библиотеке можно обращаться по адресу типа:

 

tcp://192.168.0.1:4040/Phones

 

Для настройки клиента достаточно ввести этот адрес в свойство  DataLibraryUrl соответствующего DataSet.

 

Построение иерархии источников данных.

 

Довольно часто возникает задача построить на базе одного приложения несколько источников данных с разными свойствами (например, один источник с полными правами доступа, другой – только для чтения и.т.д). При построении распределенного приложения на базе компонентов C1 такая задача решается путем создания нескольких классов DataClass. Тогда, по логике, каждый такой класс должен содержать свой экземпляр C1SchemaDef с соответствующими настройками. Как правило, в таком случае настройка всех C1SchemaDef оказывается одинаковой, а все различия реализуются бизнес-логикой.

Для подобных случаев служит компонент C1SchemaRef. Он является как бы «повторителем» C1SchemaDef. Поэтому в проекте достаточно создать только один связанный с данными класс на базе C1SchemaDef, а все другие классы строить на базе C1SchemaRef.

 

                          

 

                                       Рис 10

 

Как видно на Рис 10 единственное свойство C1SchemaRef – ссылка на C1SchemaDef. C1SchemaRef «повторяет» все свойства схемы, но позволяет реализовать собственную бизнес-логику. Программисту только надо позаботиться, чтобы при использовании класса с C1SchemaRef уже был создан экземпляр «исходного» класса (на базе C1SchemaDef).

 

Заключение к четвертой части.

 

Мы с вами наконец закончили рассмотрение механизмов доступа к данным на базе компонентов С1Data. В сочетании с c1True DBGrid они позволяют строить высокопроизводительные приложения с продвинутым интерфейсом.

В следующей части я хочу рассказать о c1FlexGrid и об оставшихся нерассмотренными возможностях c1True DBGrid.

 
     

   
   
     
  VBNet рекомендует