Россия, г. Москва |
Промежуточная среда 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