Сообщений 1    Оценка 36 [+0/-1]         Оценить  
Система Orphus

COM+ компоненты средствами .Net

Авторы: Олег Степанов
Андрей Филёв

Источник: «Технология Клиент-Сервер»
Опубликовано: 20.11.2001
Исправлено: 05.01.2007
Введение
Разработка COM+-компонентов
Использование атрибутов
Регистрация COM+-компонентов
Тип активации
Описание
Использование COM+ компонентов
JIT-активация
Строки инициализации (constructor strings)
Пул объектов
Краткая сводка COM+ сервисов, доступных COM+-компонентам
Пример использования COM+ компонентов
Заключение

Введение

Данная статья рассматривает возможности разработки объектов, использующих такие сервисы COM+, как активация по необходимости (JIT activation), синхронизация, пул объектов (object pooling), конструирование объектов (object construction), управление транзакциями и разделяемые свойства (shared properties) на платформе .NET. .NET Framework позволяет это делать «легко и приятно», совмещая атрибутное программирование и написание «обычных» .NET классов. Классы, использующие эти сервисы, Microsoft решил назвать в .Net "обслуживаемыми компонентами (serviced components)". Доступ к сервисам COM+ из них реализуется через соответствующий API .NET Framework, который будет разобран ниже. COM+ и CLR работают вместе: COM+ обслуживает вызовы компонента и интерфейсы, а CLR выполняет реализацию.

Обслуживаемые компоненты легко могут быть включены в существующий COM+-контекст, и пользуются всеми преимуществами компонентов COM+, причем для разработчика сервисы раскрываются в очень простой форме, естественно вписывающейся в общую идеологию платформы .NET, и позволяют использовать все возможности COM+ и .NET одновременно. Таким образом, можно сказать, что обслуживаемые компоненты – это просто компоненты COM+, созданные на .Net и способные использовать преимущества новой платформы. Поэтому дальше мы будем для краткости называть их просто COM+-компонентами.

Разработка COM+-компонентов

Все основные средства для разработки COM+-компонентов, такие как классы и атрибуты, представлены в .NET Framework в пространстве имен System.EnterpriseServices. В любой .NET-класс можно добавить поддержку сервисов COM+. Для этого он должен прямо или косвенно наследоваться от класса System.EnterpriseServices.ServicesComponent. Например, в следующем коде класс MyComponent является COM+-компонентом:

using System.EnterpriseServices;
...
[ assembly: ApplicationName("MyComponent")]
...
[Transaction(TransactionOption.Disabled)]
public class MyComponent : ServicedComponent
{
    [AutoComplete]
    public void DoSomethnig()
    {
    }
}

Использование атрибутов

Из предыдущего примера видно, что для задания параметров компонента используются атрибуты. Они могут применяться как к сборке (assembly), как например ApplicationName, так и к классам (Transaction) и методам (AutoComplete). В таблице 1 перечислены основные атрибуты, предоставляемые System.EnterpriseServices для разработки COM+- компонентов.

Регистрация COM+-компонентов

Для использования COM+-компонента он должен быть доступен для COM+-приложения. Для обеспечения доступа эти компоненты должны удовлетворять следующим требованиям:

Регистрация компонента может быть произведена одним из двух способов: динамически или вручную с использованием Regsvcs.exe.

Оба способа требуют, чтобы пользователь, выполняющий регистрацию, был администратором. При динамической регистрации сборки копируются в папку приложения (в случае Web-приложения – в подкаталог /bin) и не размещаются в глобальном кэше сборок (Global Assembly Cache, GAC). Регистрация происходит в случае, если вызванный управляемым компонентом COM+-компонент не был зарегистрирован. Вызовы из неуправляемых объектов (unmanaged objects) не перехватываются, и для использования компонента с такими клиентами он должен быть зарегистрирован вручную. Информация для регистрации может быть предоставлена во время написания приложения, его выполнения, и, для ручной регистрации, во время регистрации. Информация для регистрации включает идентификатор приложения COM+, тип активации (activation type) и описание. Ниже будет подробно разобрано, каким образом данная информация может быть предоставлена.

Идентификатор приложения COM+

Указывает, в какое приложение COM+ должен быть добавлен компонент. Приложение может быть идентифицировано по имени или GUID. При ручной регистрации имя приложения может быть указано с помощью параметра командной строки /appname: приложение Regsvcs.exe. На этапе разработки имя приложения может быть указано с помощью атрибута ApplicationName. Например:

[assembly: ApplicationName("My component application")]

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

[assembly: ApplicationID("B1E18470-3581-4fbe-92F6-984C0ACC285A")]

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

При динамической регистрации использование вышеописанных атрибутов является единственным способом указать приложение. При использовании Regsvcs.exe можно указать, как идентифицировать приложение – по имени или по GUID. В отсутствие указания имени приложения, оно устанавливается как имя сборки без номера версии.

Тип активации

Тип активации показывает, должен ли компонент создаваться в процессе вызывающего объекта или в новом процессе. В первом случае он представляет собой библиотечный компонент (library), а во втором – серверный (server). Тип активации может быть указан при компиляции:

Csc /target:library /r:System.EnterpriseServices.dll MyComponent.cs

Или

Csc /target:exe /r: System.EnterpriseServices.dll MyComponent.cs

В первом случает будет создана библиотека (.dll), а во втором – исполнимый модуль (.exe).

Тип активации может быть также указан на этапе проектирования с помощью атрибута ApplicationActivation:

[assembly: ApplicationActivation(ActivationOption.Library)]

Или

[assembly: ApplicationActivation(ActivationOption.Server)]

Описание

Для описания сборки можно воспользоваться атрибутом Description:

[assembly: Description(“This is my c00l assembly”)]

Использование этого атрибута необязательно, но часто полезно для предоставления дополнительной текстовой информации.

Использование COM+ компонентов

COM+ компоненты могут использоваться управляемыми объектами так же, как и обычные управляемые объекты .NET. Приведу пример:

MyComponent.cs

using System.EnterpriseServices;
using System.Reflection;

[assembly: ApplicationName("MyComponent")]
[assembly: AssemblyKeyFileAttribute("MyComponent.snk")]

[Transaction(TransactionOption.Required)]
public class MyComponent : ServicedComponent
{
    [AutoComplete]
    public void Call( string message )
    {
        Console.WriteLine("Callee called: " + message);
    }
}

MyCaller.cs

class MyCaller
{
    public static void Main()
    {
        MyComponent component = new MyComponent();

        for (int i = 1; i <= 10; i++)
            component.Call("Calling index: " + i);
    }
}

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

sn –k MyComponent.snk

То, что ключи находятся в файле MyComponent.snk, указывается с помощью атрибута сборки AssemblyKeyFileAttribute:

[assembly: AssemblyKeyFileAttribute("MyComponent.snk")]

Далее компилируется сборка COM+-компонента:

csc /target:library /r:System.EnterpriseServices.dll MyComponent.cs

и клиента:

csc /target:exe /r:MyComponent.dll MyCaller.cs

Атрибут AutoComplete, примененный к методу Call, указывает, что для этого метода включена автоматическая обработка транзакций: компонент не должен вызывать SetCommit и SetAbort для завершения транзакции. Транзакция завершается успешно автоматически, если не было выработано исключение, и откатывается в противном случае. Теперь запустим файл MyCaller.exe и убедимся, что он выводит:

Callee called: Calling index: 1
Callee called: Calling index: 2
Callee called: Calling index: 3
Callee called: Calling index: 4
Callee called: Calling index: 5
Callee called: Calling index: 6
Callee called: Calling index: 7
Callee called: Calling index: 8
Callee called: Calling index: 9
Callee called: Calling index: 10

Чтобы убедиться в том, что мы действительно создали COM+ компонент, зайдем в Component Services и обнаружим там приложение MyComponent с компонентом MyComponent:


Заметим, что наш компонент кроме обычных COM интерфейсов реализует интерфейс _Object, специфичный для CLR.

JIT-активация

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

[JustInTimeActivation]
public class JustInTimeActivatedComponent : ServicedComponent
{
   ...
}

Приведу пример:

JITComponent.cs

using System;
using System.Reflection;
using System.EnterpriseServices;

[assembly: ApplicationName("JIT demo")]
[assembly: AssemblyKeyFileAttribute("JIT.snk")]

[JustInTimeActivation]
public class JITComponent : ServicedComponent
{
    public JITComponent()
    {
        Console.WriteLine("[JITComponent] Constructor called");
    }

    public override void Activate()
    {
        Console.WriteLine("[JITComponent] Activating");
    }

    public override void Deactivate()
    {
        Console.WriteLine("[JITComponent] Deactivating");
    }

    [AutoComplete]
    public void DoSomething()
    {
        Console.WriteLine("[JITComponent] Doing something");        
    }
}
using System;
class JITCaller
{
    public static void Main()
    {
        Console.WriteLine("[JITCaller] Before component instantiation");

        JITComponent component = new JITComponent();

        Console.WriteLine("[JITCaller] Before calling method");

        component.DoSomething();
        component.DoSomething();
    }
}

Метод DoSomething был помечен как требующий автоматического завершения транзакции, чтобы автоматически производить деактивацию по завершении метода.

Результат работы программы:

[JITCaller] Before component instantiation
[JITComponent] Constructor called
[JITComponent] Activating
[JITCaller] Before calling method
[JITComponent] Doing something
[JITComponent] Deactivating
[JITComponent] Constructor called
[JITComponent] Activating
[JITComponent] Doing something
[JITComponent] Deactivating

Строки инициализации (constructor strings)

Строки инициализации – это еще один сервис COM+, позволяющий параметризовать создание компонентов путем передачи инициализирующей строки. Для реализации этой возможности в COM+-компоненте необходимо применить к нему атрибут ConstructionEnabled, в котором также можно указать строку инициализации по умолчанию. В следующем примере создается компонент со строкой инициализации по умолчанию “Default initialization string”:

TestConStrings.cs

using System;
using System.Reflection;
using System.EnterpriseServices;

[assembly: ApplicationName("Constructor strings demo")]
[assembly: AssemblyKeyFileAttribute("TestConString.snk")]

[ConstructionEnabled(Default="Default initialization string")]
public class TestConString : ServicedComponent
{
    public TestConString()
    {
        Console.WriteLine("[TestConString] Constructor called");
    }

    public override void Construct( string constructString )
    {
        Console.WriteLine("[TestConString] Construct called with parameter " + constructString);
    }

    public void DoSomething()
    {
        Console.WriteLine("[TestConString] Doing something");        
    }
}

ConStrCaller.cs

class ConStrCaller
{
    public static void Main()
    {
        TestConString component = new TestConString();

        component.DoSomething();
    }
}

Результат работы ConStrCaller.exe:

[TestConString] Constructor called
[TestConString] Construct called with parameter Default initialization string
[TestConString] Doing something

Пул объектов

Пул объектов позволяет уменьшить число дорогостоящих по времени инициализаций компонентов путем хранения их в пуле. При возникновении необходимости в некотором компоненте он берется из пула и используется, а по завершении работы кладется обратно. Для работы с пулом объектов (object pooling), к компоненту необходимо применить атрибут ObjectPooling:

[ObjectPooling(Enabled=true, MinPoolSize=2, MaxPoolSize=5, CreationTimeout=2000)]

Параметры MinPoolSize и MaxPoolSize задают минимальный и максимальный размеры пула соответственно, а CreatingTimeOut – таймаут на создание объекта (это может понадобиться при использовании всего пула, так как в этом случае объект не будет создан, пока не освободится хотя бы один объект в пуле или не истечет интервал времени, заданный CreationTimeOut).

Класс также должен реализовывать метод CanBePooled таким образом, чтобы он возвращал true. Вот пример:

ObjectPoolingComponent.cs

using System;
using System.Reflection;
using System.EnterpriseServices;

[assembly: ApplicationName("Object pooling demo")]
[assembly: AssemblyKeyFileAttribute("ObjectPooling.snk")]

[JustInTimeActivation]
[ObjectPooling(Enabled=true, MinPoolSize=2, MaxPoolSize=5, CreationTimeout=2000)]
public class ObjectPoolingComponent: ServicedComponent
{
    public ObjectPoolingComponent()
    {
        Console.WriteLine("[ObjectPoolingComponent] Constructor called");
    }

    public override void Activate()
    {
        Console.WriteLine("[ObjectPoolingComponent] Activating");
    }

    public override void Deactivate()
    {
        Console.WriteLine("[ObjectPoolingComponent] Deactivating");
    }

    public override bool CanBePooled()
    {
        return true;
    }

    public void DoSomething()
    {
        Console.WriteLine("[ObjectPoolingComponent] Doing something");        
    }
}

ObjectPoolingCaller.cs

using System;

class ObjectPoolingCaller
{
    public static void Main()
    {
        Console.WriteLine("[ObjectPoolingCaller] Before component instantiation");

        ObjectPoolingComponent[] component = new ObjectPoolingComponent[5];

        for (int i = 0; i < 5; i++)
            component[i] = new ObjectPoolingComponent();

        Console.WriteLine("[ObjectPoolingCaller] Before calling method");

        for (int i = 0; i < 5; i++)
            component[i].DoSomething();
    }
}

Результат работы программы:

[ObjectPoolingCaller] Before component instantiation
[ObjectPoolingComponent] Constructor called
[ObjectPoolingComponent] Activating
[ObjectPoolingComponent] Constructor called
[ObjectPoolingComponent] Activating
[ObjectPoolingComponent] Constructor called
[ObjectPoolingComponent] Activating
[ObjectPoolingComponent] Constructor called
[ObjectPoolingComponent] Activating
[ObjectPoolingComponent] Constructor called
[ObjectPoolingComponent] Activating
[ObjectPoolingCaller] Before calling method
[ObjectPoolingComponent] Doing something
[ObjectPoolingComponent] Doing something
[ObjectPoolingComponent] Doing something
[ObjectPoolingComponent] Doing something
[ObjectPoolingComponent] Doing something
[ObjectPoolingComponent] Deactivating

Если же в клиенте использовать не 5, а 6 объектов (больше, чем максимальный размер пула), то вывод будет следующим:

  at System.EnterpriseServices.ServicedComponentProxyAttribute.CreateInstance(
      Type serverType)
  at System.Runtime.Remoting.Activation.ActivationServices.IsCurrentContextOK(
      Type serverType, Object[] props)
  at ObjectPoolingCaller.Main()

что иллюстрирует переполнение пула.

Краткая сводка COM+ сервисов, доступных COM+-компонентам

COM+-компонентам доступны сервисы COM+, перечисленные в таблице 2. В столбце «опции настройки» указывается, каким образом он может быть настроен из административных инструментов (administrative tools) , с помощью атрибутов и классов.

Атрибут Краткое описание Применимость Значение параметра при отсутствии атрибута Значение атрибута по умолчанию
ApplicationAccessControl Позволяет конфигурировать настройки безопасности. Сборка False True
ApplicationActivation Указывает, в каком процессе будет выполняться компонент. Сборка Library Отсутствует
ApplicationID Указывает GUID компонента. Сборка Сгенерированный GUID Отсутствует
ApplicationName Указывает имя COM+ приложения, в котором будет установлен компонент. Сборка Имя сборки Отсутствует
ApplicationQueuing Включает поддержку очередей (queues). Позволяет компоненту принимать вызовы методов из очередей сообщений Сервера Очередей Сообщений Microsoft (Microsoft Message Queue Server, MSMQ). Поддерживается только в Windows 2000. Сборка Отсутствует Отсутствует
AutoComplete Контролирует автоматическое завершение или откат транзакций. Метод False True
ComponentAccessControl Включает проверку безопасности при вызовах компонента. Класс False True
COMTIIntrinsics Используется для поддержки Интегратора Транзакций COM (COM Transaction Integrator, COMTI) Класс False True
ConstructionEnabled Позволяет использовать создание объектов COM+ (COM+ object construction). Поддерживается только в Windows 2000. Класс False True
Description Описывает соответствующий объект. Сборка
Класс
Метод
Интерфейс
Отсутствует Отсутствует
EventClass Объявляет класс как класс событий (event class). Поддерживается только в Windows 2000. Класс ОтсутствуетFireInParallel = False
AllowInputSubscribers = True
PublisherFilter = Null
EventTrackingEnabled Включает отслеживание событий. Поддерживается только в Windows 2000. Класс False True
ExceptionClass Указывает объект, который будет вызван при сбоях. Класс Отсутствует Отсутствует
InterfaceQueuing Включает поддержку очередей для интерфейсов. Класс
Интерфейс
False True
JustInTimeActivation Управляет поддержкой JIT-активации. Класс False True
MustRunInClientContext Указывает, что компонент должен быть создан в контексте клиента. Класс False True
ObjectPooling Включает поддержку пула объектов для компонента. Класс False True
SecurityRole Конфигурирует роль (role) для объекта. Сборка
Класс
Метод
Отсутствует Отсутствует
Synchronization Устанавливает значение синхронизации для компонента. Класс False SynchronizationOption.Disabled
Transaction Устанавливает тип транзакции. Класс False TransactionOption.Disabled

Пример использования COM+ компонентов

Приведем пример использования COM+ компонентов. Допустим, у нас есть база данных, хранящая новостные сообщения и клиентское приложение, которое позволяет добавлять новости. Для представления новости мы создадим объект NewsComponent:

NewsComponent.cs

using System;
using System.Reflection;
using System.EnterpriseServices;
using System.Data;
using System.Data.SqlClient;
[assembly: ApplicationName("News Portal")]
[assembly: AssemblyKeyFileAttribute("NewsComponent.snk")]

[JustInTimeActivation]
[ObjectPooling(Enabled=true, MinPoolSize=2, MaxPoolSize=5, CreationTimeout=2000)]
public class NewsComponent: ServicedComponent
{
    public override void Activate()
    {
    }

    public override void Deactivate()
    {
    }

    public override bool CanBePooled()
    {
        return true;
    }

    [AutoComplete]
    public void AddNews( string from, string text )
    {
        SqlConnection sqlConnection = new
        SqlConnection("server=(local);database=sample_portal;Trusted_Connection=yes");
        sqlConnection.Open();

        SqlCommand insertCommand = new SqlCommand(             
             @"INSERT INTO [news] ([Sender], [Text]) VALUES ('" + from + @"', '" + text + @"')", sqlConnection);

        insertCommand.ExecuteNonQuery();

        sqlConnection.Close();
    }
}

Этот компонент имеет метод AddNews, позволяющий добавить новость. Он использует соединение с базой данных sample_portal на Microsoft SQL Server, которая создается следующим скриптом:

Sample_portal.sql

CREATE DATABASE [sample_portal]

GO

USE [sample_portal]

CREATE TABLE [news] (ID INTEGER IDENTITY (1, 1) PRIMARY KEY, Sender VARCHAR(100), Text VARCHAR(1000))

Этот компонент используется клиентским приложением (мы не стали писать продвинутое приложение, ограничившись простым консольным интерфейсом, чтобы сконцентрировать внимание на теме статьи):

NewsUser.cs

        name = Console.ReadLine();

        Console.Write("Enter message text: ");
        text = Console.ReadLine();

        NewsComponent c = new NewsComponent();

        c.AddNews(name, text);

        ServicedComponent.DisposeObject(c);
    }
}

Как мы видим, программа создает экземпляр компонента NewsComponent, который используется для добавления новости. По окончании использования, компонент насильственно освобождается. Этого можно и не делать, так как неиспользуемые компоненты автоматически освобождаются сборщиком мусора (garbage collector), но так как эти компоненты берутся из пула, переполнение которого ни к чему хорошему не приведет, мы его освобождаем вручную.

Заключение

Microsoft .NET Framework предоставляет простой способ разработки COM+ компонентов, позволяя в полной мере использовать все сервисы COM+. При этом разработчик избавляется от необходимости вручную реализовывать некоторые интерфейсы и (в большинстве случаев) регистрировать компоненты.


Впервые статья была опубликована в журнале <Технология Клиент-Сервер>.
Эту и множество других статей по программированию, разработке БД, многоуровневым технологиям (COM, CORBA, .Net, J2EE) и CASE-средствам вы можете найти на сайте www.optim.su и на страницах журнала.
    Сообщений 1    Оценка 36 [+0/-1]         Оценить