COM+ и .NET – практический подход – часть 3

ОГЛАВЛЕНИЕ

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

Предыдущие части

•    Первая часть
•    Вторая часть

Первая часть рассматривала все стороны использования COM+. После ее прочтения вы узнаете, какие возможности есть для размещения сборки в COM+, влияния на производительность приложения ASP.NET и каковы, при наличии таковых, ограничения, налагаемые выбором определенного варианта размещения в COM+. Вторая часть описывала все управляющие преимущества, которые приложение может получить от COM+. Эта часть содержит подробности об улучшении доступности и стабильности с помощью COM+ и о том, кого надо отслеживать и как использовать данные слежения для быстрого и легкого обнаружения источников проблемы.

Уведомить других при изменении состояния приложения

Слабосвязанные события (LCE) – одно из самых полезных средств COM+. LCE позволяет компоненту COM+ инициировать событие, которое в итоге будет возбуждено для неизвестных клиентов. Клиенты должны зарегистрироваться как подписчики компонента события COM+ с помощью COM+ MMC или динамически. Издатель вызывает один из методов компонентов события COM+, вызывающих отправку и возбуждение события во всех подписчиках. Контракт между подписчиками компонента события и издателем является единственным интерфейсом, который должны реализовывать все стороны контракта.

LCE может использоваться для создания уведомления для компонентов или приложения, не зная заранее, кем будут те приложения или компоненты. Чтобы эти приложения или компоненты могли получать уведомления, они должны реализовать тот интерфейс события и подписаться на компонент события COM+. О любой другой задаче, требующей выполнения, заботится COM+.

Для имитации мощи LCE будет создан механизм, позволяющий веб-приложению получить уведомление при изменении определенной таблицы базы данных одним из веб-приложений или любым другим приложением. Для обеспечения этого механизма надо контролировать доступ всех приложений к базе данных. Эту задачу можно осуществить путем создания нового серверного приложения COM+, реализующего функции выборки, вставки, обновления и удаления. Каждый класс слоя данных в каждом приложении должен использовать этот класс при отправке инструкций SQL в базу данных. Этот класс канала базы данных является издателем. Каждая команда базы данных будет заканчиваться вызовом класса, зарегистрированного как COM+ и служащего классом события. Каждое веб-приложение, нуждающееся в уведомлении, должно реализовать класс подписчика и зарегистрировать его (класс подписчика) как подписчик события.

Следует начать с компонента события – LCEventLibrary.Dll. Это простоя сборка, содержащая интерфейс и класс, регистрирующийся как библиотека COM+, служащая классом события, использующим атрибут EventClass. Этот класс не должен ничего реализовывать и не должен вызываться напрямую. COM+ использует этот класс для вызова подписчиков.

public interface IMySink
{
   void OnDBChange(string Action, string DBEntity);
}
[EventClass(FireInParallel = true)]
public class MyEventClass : ServicedComponent,IMySink

   public void OnDBChange(string Action, string DBEntity)
   {
      throw(new
NotImplementedException("You should not call an event class directly! "));
   }
}

Чтобы обеспечить подписку веб-приложения на MyEventClass, создается служебный класс (TransientSubscription), инкапсулирующий все строки кода, написанные для динамического добавления подписчика. Динамическая подписка производится путем манипулирования метаданными COM+. Для манипулирования метаданными COM+ необходимо использовать библиотеку типа администрирования COM+ 1.0, поэтому используется tlbimp.exe для создания управляемой обертки для этой библиотеки COM. TransientSubscription предоставляет статическую функцию для добавления и удаления подписчиков. Чтобы добавить подписчика, надо отправить уникальное имя, тип класса события и класс подписчика. Интерфейс приемника находится путем обхода в цикле интерфейсов класса подписчика.

static public void Add(string subName,Type eventClass,Type sinkInterface,
string method,object subscriber)
{
   Type sinkType = subscriber.GetType();
   if(method != "")
   {
      MethodInfo info = sinkInterface.GetMethod(method);
   }
   //добавление новой временной подписки на каталог
   ICOMAdminCatalog catalog;
   ICatalogCollection transientCollection;
   ICatalogObject subscription = null;
   catalog = (ICOMAdminCatalog)new COMAdminCatalog();
   transientCollection =
(ICatalogCollection)catalog.GetCollection("TransientSubscriptions");
   subscription = (ICatalogObject)transientCollection.Add();
   subscription.set_Value("Name",subName);
   subscription.set_Value("SubscriberInterface",subscriber);
   string eventClassString = "{"+eventClass.GUID.ToString()+"}";
   subscription.set_Value("EventCLSID",eventClassString);
   string sinkString = "{" +sinkInterface.GUID.ToString()+ "}";
   subscription.set_Value("InterfaceID",sinkString);
   subscription.set_Value("MethodName",method);
   subscription.set_Value("FilterCriteria","");
   subscription.set_Value("PublisherID","");
   transientCollection.SaveChanges();
}

Класс подписчика (MySubscriber) реализуется в составе веб-приложения. MySubscriber реализует интерфейс IMySink и пишет код, вызываемый, когда класс события вызывает своих подписчиков. В данном примере просто обновляется переменная приложения, чтобы указать, что что-то изменилось. Проблема обновления объекта приложения требует обходного решения. Нельзя использовать HTTPContext.Current, так как вызов подписчика не подключается ни к какому запросу. Для обхода этой проблемы был добавлен конструктор, получающий HttpApplicationState в качестве параметра и устанавливающий внутренний член в него.

public class MySubscriber : LCEventLibrary.IMySink 
{
   private System.Web.HttpApplicationState App;
   public MySubscriber()
   {
   }
   public MySubscriber(System.Web.HttpApplicationState inApp)
   {
      App = inApp;
   }
   public void OnDBChange(string Action, string DBEntity)
   {
      App["DbChange"] = true;
   }
}

Фактическая подписка происходит в событии Application_start.

m_Subscriber = new MySubscriber(System.Web.HttpContext.Current.Application);
TransientSubMgr.TransientSubscription.Add
("MySub",typeof(LCEventLibrary.MyEventClass),m_Subscriber);
Application["DbChange"] = false;

Сборка PublisherClass содержит класс PubClass, играющий роль издателя. PubClass служит  единственной точкой подключения к базе данных, регистрируясь как приложение COM+ с методами для всех действий базы данных. Каждый метод создает экземпляр MyEventClass на базе интерфейса IMySink и вызывает OnDBChange с правильными параметрами. UpdateData извлекает имя таблицы из инструкции SQL и отправляет его в качестве параметра.

public class PubClass : System.EnterpriseServices.ServicedComponent 
{
   public PubClass()
   {
   }
   public void UpdateData(string SQL)
   {
      LCEventLibrary.IMySink sink;
      sink = new LCEventLibrary.MyEventClass();
      string[] arr = SQL.Split (' ');
      sink.OnDBChange("Update",arr[1]);
   }

Для активации издателя добавляется новая страница (CallPublisher.aspx) в веб-приложение. Эта страница содержит две кнопки: одну –  для вызова метода UpdateData PubClass и другую – для обновления страницы. На page_load проверяется переменная DbChange приложения. Если она установлена в истину, выполняется одно из действий базы данных и уведомление отображается в виде HTML.

   private void Page_Load(object sender, System.EventArgs e)
   {
      if ((bool)Application["DbChange"] == true)
      {
         Response.Write ("DB data changed!");
      }
   }
   private void Button1_Click(object sender, System.EventArgs e)
   {
      PublisherClass.PubClass oPC = new PublisherClass.PubClass();
      oPC.UpdateData("update MyTable set MyField=value");
   }
   private void Button2_Click(object sender, System.EventArgs e)
   {
   }
 

Запуск страницы впервые покажет форму с двумя кнопками. Нажатие на кнопку “Обновить” заставит издателя вызвать метод класса события с последующим вызовом всех подписчиков. Класс подписчика в веб-приложении обновит данную переменную приложения. Нажатие кнопки обновления теперь выдаст страницу, содержащую текстовое уведомление об изменении.

Снятие плода COM+ без ухода за деревом COM+.(COM+ 1.5 / CLR 1.1)

COM+ 1.5 вводит новую схему работы, позволяющую строителям классов использовать часть служб COM+ без регистрации классов как приложения COM+ (службы без компонентов - SWC). Эта возможность введена для разработчиков C++, главной заботой которых является производительность. Разработчики C++ могут использовать класс CServiceConfig для запроса интерфейса одной из служб и затем использовать API, чтобы использовать выбранные службы COM+ в качестве встроенного блока или в пакетном режиме.

Хотя SWC может использоваться приложением .NET, использующим стыкуемость, CLR 1.1 вводит новый класс (ServiceConfig) в пространстве имен EnterpriceServices, позволяющий использовать COM+ SWC посредством объектов .NET. ServiceConfig инкапсулирует требуемые функции API для обеспечения SWC. Использовать этот класс просто. Надо создать экземпляр, установить атрибуты для установки средств, требующих использования, с помощью “Войти” и “Уйти” пометить блок, который будет использовать  средства COM+, и написать код внутри этого блока.

   public void HandleUser(string UID)
   {
      DataLayerClass oDLC = new DataLayerClass();
      try
      {
         ServiceConfig sc = new ServiceConfig();
         sc.Transaction = TransactionOption.RequiresNew;
         sc.TransactionTimeout = 30;
         sc.TrackingAppName = "SWC Test";
         sc.TransactionDescription = "Test";
         sc.TrackingEnabled = true;
         //начать блок
         ServiceDomain.Enter(sc);
         // вызвать слой данных для установки базы данных
         oDLC.DeleteUser(UID);
         ContextUtil.SetComplete();
      }
      catch(Exception ex)
      {
         ContextUtil.SetAbort();
      }
      finally
      {
         // закончить блок
         ServiceDomain.Leave();
      }
   }

SWC активирует лишь часть служб COM+, точнее, SWC использует следующие службы COM+:
•    COMTIIntrinsic
•    IISIntrinsic
•    Переключение контекста
•    Уровень изоляции
•    Сегменты
•    Соседние сборки
•    Синхронизация
•    Пул потоков
•    Транзакции.

Однако в его длинный список атрибутов самые полезные службы, вроде организации пула объектов, JITA, компоненты с поддержкой очередей и слабосвязанные события, не включены. Эти средства COM+ решают повседневные проблемы и повышают надежность и доступность приложения.