Создание подключаемой инфраструктуры - Создание подключаемого модуля
ОГЛАВЛЕНИЕ
Шаг 1. Создание простого текстового редактора
Я не буду надоедать вам с деталями. Вы все найдете в исходном коде: это всего лишь простая форма, показывающая кусок текста. С этого момента я предполагаю, что вы уже создали простое приложение.Шаг 2. Создание подключаемого SDK
Теперь, когда у вас есть приложение, вы захотите, чтобы оно могло обмениваться информацией с внешним подключаемым модулем. Как это сделать?Решение в том, чтобы сориентировать работу приложения на опубликованный интерфейс, набор общих членов и методов, которые будут реализовываться всеми специальными подключаемыми модулями. Я назову этот интерфейс IPlugin. С этих пор любой разработчик, который захочет создать подключаемый модуль для вашего приложения, должен будет реализовать этот интерфейс. Он будет располагаться в общей библиотеке, которую будут использовать и ваше приложение, и любой специальный подключаемый модуль.
Чтобы описать этот интерфейс, вам нужны просто некоторые данные о вашем простом подключаемом модуле: его имя и метод, который будет давать модулю указания осуществлять универсальные действия на основании данных вашего приложения.
public interface IPlugin
{
string Name{get;}
void PerformAction(IPluginContext context);
}
Код прост, но зачем передавать интерфейс IPluginContext в PerformAction? Причина, по которой вы посылаете интерфейс, а не просто строку — обеспечение большей гибкости в отношении того, какой объект вы сможете посылать. В настоящее время, интерфейс очень прост:
public interface IPluginContext
{
string CurrentDocumentText{get;set;}
}
Теперь все, что вам надо сделать — реализовать этот интерфейс в одном или более объектов и отправить их в любой подключаемый модуль для получения результата. В будущем это позволит вам изменять строку не только текстового окна, но любого объекта.
Шаг 3. Создание вашего специального подключаемого модуля
Все, что вам сейчас надо сделать:- Создать отдельный объект библиотеки классов.
- Создать класс, который реализовывает интерфейс IPlugin.
- Откомпилировать этот класс и поместить его в ту же папку, к которой находится главное приложение.
public class EmailPlugin:IPlugin
{
public EmailPlugin()
{
}
// Единственная точка для входа в ваш подключаемый модуль
// Принимает объект IPluginContext,
// в котором находится текущее
// содержимое редактора.
// Затем он проводит синтаксический разбор текста, обнаруженного в
// редакторе, и оставляет в нем только обнаруженные электронные адреса.
public void PerformAction(IPluginContext context)
{
context.CurrentDocumentText=
ParseEmails(context.CurrentDocumentText);
}
// Имя подключаемого модуля, которое появится
// в меню «Подключаемые модули» («Plugins») редактора
public string Name
{
get
{
return "Email Parsing Plugin";
}
}
// Проводит синтаксический разбор данной строки в поисках любых адресов
// электронной почты, используя класс Regex,
// и возвращает строку, содержащую только адреса электронной почты
private string ParseEmails(string text)
{
const string emailPattern= @"\w+@\w+\.\w+((\.\w+)*)?";
MatchCollection emails =
Regex.Matches(text,emailPattern,
RegexOptions.IgnoreCase);
StringBuilder emailString = new StringBuilder();
foreach(Match email in emails)
emailString.Append(email.Value + Environment.NewLine);
return emailString.ToString();
}
}
Шаг 4. Заставьте ваше приложение знать о новом подключаемом модуле
Когда вы скомпилировали ваш подключаемый модуль, как приложение узнает о его существовании?Решение простое:
- Создайте файл конфигурации приложения.
- Создайте секцию в файле конфигурации, в которой перечислены все существующие подключаемые модули.
- Создайте синтаксический анализатор для этой секции файла конфигурации.
Чтобы завершить первый шаг, просто добавьте XML-файл в главное приложение.
Назовите этот файл App.Config. В этом случае при каждой сборке вашего приложения Microsoft® Visual Studio .NET автоматически будет копировать этот файл в выходную папку и переименовывать его в <yourApp>.Config, избавляя вас от хлопот.
Теперь разработчик подключаемого модуля должен иметь возможность легко добавить данные в файл конфигурации, чтобы опубликовать все созданные подключаемые модули. Здесь показано, как должен выглядеть файл конфигурации:<configuration>
<configSections>
<section name="plugins"
type="Royo.PluggableApp.PluginSectionHandler, PluggableApp"
/>
</configSections>
<plugins>
<plugin type="Royo.Plugins.Custom.EmailPlugin, CustomPlugin" />
</plugins>
</configuration>
Обратите внимание на тэг configSections. Он сообщает настройкам конфигурации приложения, что у вас в этом файле конфигурации есть секция подключаемых модулей, и что у вас есть синтаксический анализатор для этой секции. Он находится в классе Royo.PluggableApp.PluginSectionHandler, который располагается в сборке PluggableApp. Ниже я покажу вам код этого класса.
Далее, у вас есть секция подключаемых модулей файла конфигурации, в которой перечислены для каждого подключаемого модуля имя класса и имя сборки, в которых он находится. Вы будете использовать эту информацию при создании экземпляров подключаемого модуля позже.
Закончив файл конфигурации, вы завершаете один из этапов этого цикла. Подключаемый модуль готов к использованию, и сообщил о своем существовании во все необходимые каналы. Все, что вам осталось теперь сделать — научить ваше приложение считывать эту информацию и создавать в соответствие с нею экземпляры опубликованных подключаемых модулей.
Шаг 5. Синтаксический анализ файла конфигурации с использованием IConfigurationSectionHandler
Чтобы провести синтаксический анализ подключаемых модулей, обнаруженных в файле .config приложения, инфраструктура предлагает очень простой механизм, позволяющий вам регистрировать определенный класса в качестве обработчика определенной части вашего файла конфигурации. У вас должен быть обработчик любой части файла, для которой инфраструктура не проводит автоматического синтаксического анализа; в противном случае, возникнет ConfigurationException.Чтобы обеспечить класс, проводящий синтаксический разбор секции подключаемых модулей, вам всего лишь надо реализовать интерфейс System.Configuration.IConfigurationSectionHandler. Сам по себе этот интерфейс очень прост:
public interface IConfigurationSectionHandler
{
public object Create(object parent, object configContext, System.Xml.XmlNode section);
}
Все, что вам надо сделать — переопределить метод Create в своем специальном классе и провести синтаксический разбор XML-узла, предоставленного вам. Этот XML-узел, в данном случае, будет XML-узлом «Подключаемые модули» («Plugins»). После этого у вас есть вся информация, необходимая для создания экземпляра подключаемых модулей для вашего приложения.
Ваш специальный класс должен поставлять стандартный конструктор, поскольку его экземпляр создается инфраструктурой автоматически во время выполнения, а затем вызывается его метод Create. Вот код для класса PluginSectionHandler:
public class PluginSectionHandler:IConfigurationSectionHandler
{
public PluginSectionHandler()
{
}
// Пройдите по всем дочерним узлам
// XMLNode, который был передан вам, и создайте экземпляры
// заданных в значениях атрибутов узлов типов
// мы используем здесь try/Catch, потому что некоторые из узлов
// могут содержать неверные ссылки на тип подключаемого модуля
public object Create(object parent,
object configContext,
System.Xml.XmlNode section)
{
PluginCollection plugins = new PluginCollection();
foreach(XmlNode node in section.ChildNodes)
{
//Здесь располагается код для создания экземпляров
// подключаемых модулей и их инициализации
...
}
return plugins;
}
}
Как видите, в упомянутом ранее файле конфигурации вы предоставляете данные, необходимые инфраструктуре для обработки секции подключаемых модулей, используя тэг configSection перед тэгами самих подключаемых модулей.
<configuration>
<configSections>
<section name="plugins"
type="Royo.PluggableApp.PluginSectionHandler, PluggableApp"
/>
</configSections>
...
Обратите внимание, как определять класс. Строка состоит из двух частей: полного имени класса (включая пространство имен), запятой, имени сборки, в которой размещается этот класс. Это все, что нужно инфраструктуре для создания экземпляра класса, и не удивительно, что именно эта информация нужна и при регистрации любого подключаемого модуля для вашего приложения.