Промежуточная среда COM+ и служба Enterprise Services
Создание компенсирующего менеджера ресурсов
Компенсирующий менеджер ресурсов позволяет использовать в транзакциях среды COM+ какие либо ресурсы, которые не имеют прямой поддержки транзакций COM+. Этот механизм включает следующие классы из пространства имен System.EnterpriseServices.CompensatingResourceManager:
- журнал операций ( log ), заполняемый операциями над ресурсом;
- секретарь (класс Clerk ), ведущий журнал операций;
- компенсатор (наследника класса Compensator ), который восстанавливает первоначальное состояние ресурса в случае отката транзакции и вносит изменения в ресурс при успехе транзакции.
Сам менеджер ресурсов должен решить задачу изоляции ресурса в рамках транзакции и ведения журнала операций. При успехе транзакции компенсатор вносит постоянные изменения или отменяет их в соответствии с журналом операций. Следует отметить, что какие-либо временные изменения, производимые менеджером с ресурсом, могут вероятно нарушить требования изоляции транзакции, поскольку могут быть видимыми для внешних по отношению к транзакции объектов. С другой стороны, объекты внутри транзакции должны наблюдать происходящие с ресурсом изменения до успешного завершения транзакции.
В качестве примера менеджера ресурсов рассмотрим работу с файлами небольшого размера. Менеджер ресурсов предоставляет следующие сервисы:
- открытие документа на чтение;
- открытие документа на перезапись;
- закрыть файл с сохранением изменений.
С целью изоляции транзакций запись в файл происходит в момент успешного завершения транзакции. До этого момента данные хранятся в памяти. Такое решение безусловно не является эффективным с точки зрения траты ресурсов решение, оно используется в качестве примера. При чтении в рамках транзакции документа, уже ранее измененного в этой же транзакции, вместо чтения из файла происходит чтение последней относящийся к этому файлу записи из журнала операций. Журнал операций хранит пары из имени файла (в нижнем регистре) и содержимого файла.
Пример компилируется и запускается make файлом, который можно передать как параметр входящей в состав .NET SDK утилите nmake.exe. Этот файл имеет следующее содержание.
# Файл: makefile
all: CrmSample.exe
# сборка должна быть подписана
CrmSample.key:
sn -k CrmSample.key
CrmSample.exe: CrmSample.cs CrmSample.Key
csc /r:System.EnterpriseServices.dll CrmSample.cs
/keyfile:CrmSample.key
install:
# установить приложение COM+
regsvcs CrmSample.exe
uninstall:
regsvcs –u CrmSample.exeПоскольку для регистрации сборки как приложения COM+необходимо, чтобы она была подписана, то вызовом утилиты sn.exe из состава .NET SKD создается пара ключей. Затем компилятор языка C# csc.exe создает сборку, используя этот файл ключей. При команде nmake install сборка устанавливается в качестве приложения COM+ вызовом утилиты regsvcs.exe Microsoft Windows.
Далее будет рассмотрено содержание файла CrmSample.cs.
// Файл CrmSample.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.EnterpriseServices;
using System.EnterpriseServices.CompensatingResourceManager;
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl(false)]
[assembly: ApplicationCrmEnabled]
[assembly: ApplicationName("Seva CRM")]
[assembly: Description("Пример на использование CRM")]Данные атрибуты сборки из пространства имен System.EnterpriseServices управляют параметрами создаваемого приложения COM+:
- ApplicationActivation – задает тип приложения COM+ (серверный или библиотечный);
- ApplicationCrmEnabled – необходим для использования CRM в приложении COM+;
- ApplicationAccessControl – управляет контролем доступа к приложению, в данном примере – отключен;
- Атрибут System.ComponentModel.DescriptionAttribute задает описание сборки.
Класс StreamLog содержит статический метод для записи в файл буфера, вызываемый при завершении транзакции.
public static class StreamLog
{
public static void Save(LogRecord log)
{
if (log.Record is object[])
{
object[] record = (object[])log.Record;
string fileName = (string) record[0];
byte[] buffer = (byte[]) record[1];
using (FileStream file = new FileStream(fileName,
FileMode.Create, FileAccess.Write))
{
file.Write(buffer, 0, buffer.Length);
}
}
} // Save()
} // StreamLog