Инстанцирование замещающего класса выполняется через фабрику объектов замещающих классов. У фабрики запрашивается экземпляр замещаемого типа. В результате возвращается экземпляр соответствующего замещающего типа, который вычисляется фабрикой по дереву зависимостей типов в схемах исходных кодов.
Чтобы создать замещающий конфигурационный элемент соответствующего типа, воспользуйтесь инструкцией, которая приведена в статье Разработка конфигурационных элементов.
Атрибут [Override]
Тип атрибута [Override] принадлежит пространству имен Terrasoft.Core.Factories и является прямым наследником базового типа System.Attribute. Пространство имен Terrasoft.Core.Factories описано в Библиотеке .NET классов. Базовый тип System.Attribute описан в официальной документации Microsoft.
Атрибут [Override] применяется только к классам. Назначение атрибута [Override] — определение классов, которые должны учитываться при построении дерева зависимостей замещающих и замещаемых типов фабрики.
Ниже рассмотрено применение атрибута [Override] к замещающему классу MySubstituteClass. SubstitutableClass — замещаемый класс.
Класс ClassFactory
Назначение статического класса ClassFactory — реализация фабрики по созданию замещающих объектов Creatio. Фабрика использует open-source фреймворк внедрения зависимостей Ninject. Статический класс ClassFactory описан в Библиотеке .NET классов.
Инициализация фабрики осуществляется в момент первого обращения к ней, то есть при первой попытке получить экземпляр замещающего типа. При инициализации фабрика собирает информацию обо всех замещаемых типах конфигурации.
Алгоритм работы фабрики:
- Поиск замещающих типов. Выполняется фабрикой путем анализа типов конфигурационной сборки. Класс, помеченный атрибутом [Override], интерпретируется фабрикой как замещающий, а родитель этого класса — как замещаемый.
- Формирование дерева зависимостей типов в виде списка пар значений Замещаемый тип —> Замещающий тип. При этом в дереве иерархии замещения не учитываются промежуточные типы.
- Замещение исходного класса последним наследником в иерархии замещения.
- Выполнение привязки типов замещения в соответствии с построенным деревом зависимостей типов. Используется фреймворк Ninject.
Рассмотрим работу фабрики на примере иерархии классов, которая приведена ниже.
По иерархии классов будет построено дерево зависимостей, которое приведено ниже.
При построении дерева зависимостей не будут учтены промежуточные типы.
Здесь ClassA замещается типом ClassC, а не промежуточным типом ClassB. Это связано с тем, что ClassC — последний наследник в иерархии замещения. Таким образом, при запросе экземпляра типа ClassA или ClassB фабрика возвращает экземпляр ClassC.
Экземпляр замещаемого типа
Чтобы получить экземпляр замещаемого типа, необходимо использовать публичный статический параметризованный метод Get<T>. Этот метод предоставляет фабрика ClassFactory. В качестве обобщенного параметра метода выступает замещаемый тип. Метод Get<T> описан в Библиотеке .NET классов.
В результате будет создан экземпляр класса MySubstituteClass. Нет необходимости явно указывать тип создаваемого экземпляра, поскольку, благодаря предварительной инициализации, фабрика определяет, какой именно тип замещает запрашиваемый тип и создает соответствующий ему экземпляр.
В качестве параметров метод Get<T> может принимать массив объектов ConstructorArgument. Каждый объект массива представляет собой аргумент конструктора класса, который создан с помощью фабрики. Таким образом, фабрика позволяет инстанцировать замещающие объекты с параметризованными конструкторами. При этом ядро фабрики самостоятельно разрешает все зависимости, необходимые для создания или работы объекта.
Рекомендуется, чтобы конструкторы замещающего класса имели сигнатуру, соответствующую сигнатуре замещаемого класса. Если логика реализации замещающего класса требует объявления конструктора с пользовательской сигнатурой, необходимо соблюдать правила, которые приведены ниже.
Правила создания и вызова конструкторов замещаемого и замещающего классов:
- Если замещаемый класс не имеет явно реализованного параметризованного конструктора (имеет только конструктор по умолчанию), то в замещающем классе допускается явная реализация своего конструктора без каких-либо ограничений. При этом соблюдается стандартный порядок вызовов конструкторов родительского (замещаемого) и дочернего (замещающего) классов. В этом случае необходимо помнить, что при инстанцировании замещаемого класса через фабрику ей необходимо передавать корректные параметры для инициализации свойств замещающего класса.
- Если замещаемый класс имеет параметризованный конструктор, то в замещающем классе необходимо реализовать конструктор. Конструктор замещающего класса должен явно вызывать параметризованный конструктор родительского (замещаемого) класса, которому передаются параметры для корректной инициализации родительских свойств. При этом конструктор замещающего класса может выполнять инициализацию своих свойств либо оставаться пустым.
Несоблюдение правил приводит к ошибке времени выполнения. Ответственность за корректность инициализации свойств замещающего и замещаемого классов лежит на разработчике.
Пример 1
Замещаемый класс SubstitutableClass имеет один конструктор по умолчанию.
В замещающем классе SubstituteClass объявлены два конструктора — конструктор по умолчанию и параметризированный. Замещающий класс переопределяет родительский метод GetMultipliedValue().
Примеры получения экземпляра замещающего класса SubstituteClass через фабрику представлены ниже.
Пример 2
Замещаемый класс SubstitutableClass имеет один параметризированный конструктор.
Замещающий класс SubstituteClass также имеет один параметризированный конструктор.
Пример создания и использования экземпляра замещающего класса SubstituteClass через фабрику представлен ниже.
Пример 3
Замещаемый класс SubstitutableClass имеет один параметризированный конструктор.
В замещающем классе SubstituteClass переопределяется метод GetMultipliedValue(), который будет возвращать фиксированное значение. Класс SubstituteClass не требует первичной инициализации своих свойств, в нем должен быть явно объявлен конструктор, который вызывает родительский конструктор с параметрами для корректной инициализации родительских свойств.
1. Реализовать замещаемый класс
- Перейдите в раздел Конфигурация (Configuration) и выберите пользовательский пакет, в который будет добавлена схема.
-
На панели инструментов реестра раздела нажмите Добавить —> Исходный код (Add —> Source code).
-
В дизайнере схем заполните свойства схемы:
- Код (Code) — "UsrOriginalClass".
- Заголовок (Title) — "OriginalClass".
-
Создайте замещаемый класс UsrOriginalClass, который содержит виртуальный метод GetAmount(int, int). Метод выполняет суммирование двух значений, переданных в качестве параметров.
- На панели инструментов дизайнера нажмите Сохранить (Save), а затем Опубликовать (Publish).
2. Реализовать замещающий класс
- Перейдите в раздел Конфигурация (Configuration) и выберите пользовательский пакет, в который будет добавлена схема. Для замещающего класса используйте пакет, отличный от пакета, в котором реализован замещаемый класс UsrOriginalClass.
- В зависимость пользовательского пакета с замещающим классом добавьте пользовательский пакет с замещаемым классом UsrOriginalClass, созданный на предыдущем шаге.
-
На панели инструментов реестра раздела нажмите Добавить —> Исходный код (Add —> Source code).
-
В дизайнере схем заполните свойства схемы:
- Код (Code) — "UsrSubstituteClass".
- Заголовок (Title) — "SubstituteClass".
-
Создайте замещающий класс UsrSubstituteClass, который содержит метод GetAmount(int, int). Метод выполнит суммирование двух значений, переданных в качестве параметров, и умножит полученную сумму на значение, переданное в свойстве Rate. Первичная инициализация свойства Rate будет выполняться в конструкторе замещающего класса.
- На панели инструментов дизайнера нажмите Сохранить (Save), а затем Опубликовать (Publish).
3. Реализовать пользовательский веб-сервис
- Перейдите в раздел Конфигурация (Configuration) и выберите пользовательский пакет, в который будет добавлена схема. Для пользовательского веб-сервиса используйте пакет, в котором реализован замещаемый класс UsrOriginalClass.
-
На панели инструментов реестра раздела нажмите Добавить —> Исходный код (Add —> Source code).
-
В дизайнере схем заполните свойства схемы:
- Код (Code) — "UsrAmountService".
- Заголовок (Title) — "AmountService".
-
Создайте класс сервиса.
- В дизайнере схем добавьте пространство имен Terrasoft.Configuration.
- C помощью директивы using добавьте пространства имен, типы данных которых будут задействованы в классе.
- Добавьте название класса UsrAmountService, которое соответствует названию схемы (свойство Код (Code)).
- В качестве родительского класса укажите класс Terrasoft.Nui.ServiceModel.WebService.BaseService.
- Для класса добавьте атрибуты [ServiceContract] и [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)].
-
Реализуйте метод класса.
В дизайнере схем добавьте в класс метод public string GetAmount(int value1, int value2), который реализует конечную точку пользовательского веб-сервиса. Метод GetAmount(int, int) выполнит суммирование двух значений, переданных в качестве параметров.
Исходный код пользовательского веб-сервиса UsrAmountService представлен ниже.
В коде приведены примеры создания экземпляра замещающего класса и через фабрику замещаемых объектов, и через оператор new().
- На панели инструментов дизайнера нажмите Сохранить (Save), а затем Опубликовать (Publish).
В результате в Creatio появится пользовательский веб-сервис UsrAmountService типа REST с конечной точкой GetAmount.
Результат выполнения примера
Чтобы вызвать пользовательский веб-сервис, из браузера обратитесь к конечной точке GetAmount веб-сервиса UsrAmountService и в качестве параметров value1 и value2 передайте 2 произвольных числа.
В свойстве GetAmountResult будет возвращен результат работы пользовательского веб-сервиса без замещения классов и с замещением классов.