dotSITE
Шаблоны проектирования Work in Murano Software. Вопросы/Ответы
новости материалы решения форумы группы настройки/о проекте
Логин/Регистрация
Логин:
Пароль:
Запомнить вас:
Регистрация
Забыли пароль?

Комментарии

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

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

Введение

Данная татья рассматривает возможности разработки объектов, использующих такие сервисы 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+-приложения. Для обеспечения доступа эти компоненты должны удовлетворять следующим требованиям:

Сборки должны иметь сильное имя (strong name).Сборка должна быть зарегистрирована в реестре Windows.Объявления библиотеки типов должны быть зарегистрированы и установлены в приложение 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");
    }
}            
        

JITCaller.cs

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();
    }
}
        

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


Контакт Реклама на сайте Спонсорам Веб мастерам

Лицензионное соглашение - © 2000-2012 dotSITE
Хостинг .NET предоставлен PARKING.RU
Поддержку сайта осуществляет Murano Software Inc., Offshore software development