3. Теперь все готово для реализации самой WCF-службы. В файле WcfService.cs создадим новый класс WcfService. Этот класс будет отвечать за создание, запуск и остановку службы WCF. Данный класс я реализовал на основе шаблона проектирования Singleton. Более подробно с шаблонами проектирования, можно ознакомиться по этой ссылке:
.
Почему Singleton? Суть выбора в том, что чтобы не усложнять наше приложение я буду размещать только одну службу, доступную по определенному адресу. Используя этот шаблон, я на объектном уровне ограничиваю возможность создания нескольких экземпляров класса службы и тем самым гарантирую, что в рамках приложения будет работать только одна служба WCF. (На самом деле существует возможность разместить несколько служб WCF в контексте одного приложения, но тогда для каждй службы в отдельности нужно на программном уровне прописывать адреса доступа и их оконечные точки)
/*
* Класс реализации запуска WCF-сервиса.
* Реализован с использованием шаблона Singleton
*/
public sealed class WcfService
{
private static WcfService _WcfService;
private ServiceHost _SvcHost;
public static WcfService Service
{
get
{
_WcfService = _WcfService ?? new WcfService();
return _WcfService;
}
}
// Конструктор по умолчанию определяется как private
private WcfService()
{
// Регистрация сервиса и его метаданных
_SvcHost = new ServiceHost(typeof(MyWcfService));
}
public void Start()
{
_SvcHost.Open();
}
public void Stop()
{
_SvcHost.Close();
}
}
Класс содержит статический закрытый член экземпляра этого же класса, а так же свойство, при обращении к которому происходит проверка закрытого члена _WcfService и в случае, если он не определен - происходит вызов конструктора. Сам конструктор тоже является закрытым. Это гарантирует невозможность создания объекта нашего класса извне.
Вторым закрытым членом является переменная _SvcHost с типом ServiceHost. Это и есть наша служба WCF. В закрытом конструкторе происходит создание рабочего экземпляра службы. Обратите внимание, что в конструктор мы передаем контракт, тем самым сообщая службе, что она должна предоставлять для клиентов методы этого контракта.
Так же класс службы содержит два метода: Start() - запуск WCF службы и Stop() - ее остановка. Вот и все, класс службы реализован.
4. Последним шагом создания WCF-службы является определение базового адреса, по которому она будет доступна и оконечных точек (endpoints), то есть каналов, по которым мы можем доступиться к службе. Все эти настройки я вынес в конфигурационный файл. Использование настроек в конфигурационном файле делает сервис намного гибче и масштабируемей (не нужна перекомпиляция программы при каждом изменении настроек).
Добавим в наш проект файл конфигурации app.config. Следующий листинг показывает определение настроек службы WCF в конфигурационном файле:
При запуске служба просматривает конфигурационный файл приложенияapp.config (web.config) и ищет тег system.serviceModel. В этом теге определяются следующие настройки:
- Тег service определяет имя службы (пространство имен и имя контракта), а так же имя так называемой конфигурации поведения службы. Ниже в настройках присутствует тег behaviors. На самом деле, в этом теге имеется масса интересных настроек поведения служб WCF, о которых лучше подробно поговорить в отдельной статье. Здесь же я использую этот тег в конфигурации только для того, чтобы включить в нашу службу описание ее методов, что позволяет получать полную информацию о службе WCF клиентскому приложению (стандартное описание на языке WSDL).
- Тег host содержит список базовых адресов службы. Я определяю адрес на основе протокола http для доступа к методам службы
- Далее определяются теги оконечных точек (endpoints). Обратите внимание - я определил две точки. Первая точка использует привязку basicHttpBinding и контракт WcfHosting.IMyWcfService. А вторая точка используется для получения описания службы (о чем говорилось выше). Такая точка называется точка обмена метаданными (mex). Она имеет жестко заданные в объектной модели WCF адрес mex (это относительный адрес от базового, определенного в теге host), привязку (mexHttpBinding) и контракт (IMetadataExchange). Наличие такой точки в службе позволяет клиенту получить подробное описание всех методов службы, их типов и возвращаемых значений. Среда Visual Studio позволяет очень легко получить описание службы через эту точку. Ниже я приведу пример как это можно сделать.
Все, наша служба полностью готова. Теперь остается только ее разместить внутри нашего консольного приложения. Как я ранее уже писал, консольное приложение будет иметь возможность простого запуска, а так же может быть установлено как сервис ОС Windows. Для того, чтобы наше приложение могло быть установлено и запущено как сервис ОС, в проект нужно добавить элемент
WindwosService. Я назвал его
WinService.cs.
Класс WinService наследуется от родительского класса ServiceBase и имеет дваoverride-метода OnStart и OnStop. Первый срабатывает, когда сервис стартует, второй - когда останавливается. Определим в классе закрытый член нашей службы WCF. В методе OnStart пропишем запуске WCF-службы, в OnStop - ее остановке
:
partial class WinService : ServiceBase
{
private WcfService _Wcf;
public WinService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
_Wcf = WcfService.Service;
_Wcf.Start();
}
catch (Exception ex)
{
EventLog.WriteEntry(ex.Message, System.Diagnostics.EventLogEntryType.Information);
}
}
protected override void OnStop()
{
if (_Wcf != null)
{ _Wcf.Stop(); }
}
}
Код сервиса готов! Теперь определим установщик для сервиса Windows. Самый простой способ это следать - это кликнуть по элементу WinService и в окне пустого дизайнера открыть правой кнопкой мыши контекстное меню и выбрать пункт Add Installer.
Это автоматически добавит в проект файл ProjectInstaller.cs. В дизайнере этого файла будут определены два элемента: Service (содержит имя сервиса, его описание, а так же режим запуска) и Process (процесс, в контексте которого будет запущен сервис). Переименуйте их как вам будет угодно и в свойствах Service укажите имя, описание и тип запуска сервиса.
Последний этап - реализуем запуск WCF-службы в режиме консоли и в режиме сервиса Windows. Откроем в проекте файл Program.cs и напишем следующий код:
static void Main(string[] args)
{
// Если запускает пользователь сам
if (Environment.UserInteractive)
{
WcfService s = WcfService.Service;
s.Start();
while (Console.ReadLine().ToLower() != "exit") { }
s.Stop();
return;
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new WinService() };
ServiceBase.Run(ServicesToRun);
}
}
При запуске программы пользователем (проверяем переменную Environment.UserInteractive) мы вызываем наш Singleton-класс, где реализована служба WCF и вызываем метод Start(). В случае же запуска приложения в контексте сервиса Windows мы вызываем метод Run класса ServiceBase.
Все! Чтобы сервис появился в оснастке Service Control Management воспользуйтесь утилитой InstallUtil, входящей в пакет .NET SDK utility. С помощью этой утилиты можно установить/удалить любой сервис в оснастку Service Control Management.
В итоге мы получили следующее: WCF-служба размещена в контексте консольного приложения и сервиса ОС, доступна по адресу http://localhost:9000/WcfHosting, а ее описание доступно по адресу http://localhost:9000/WcfHosting/mex. Служба работает через протокол http.
Обратиться к службе очень просто. Для этого в любом проекте Visual Studio кликните правой кнопкой мыши по элементу Service Reference и выберите пунктAdd Service Reference.
В открывшемся окне введите http://localhost:9000/WcfHosting/mex и нажмите Go:
Итак, среда Visual Studio видит службу WCF, а так же все ее методы. Это все доступно, благодаря оконечной точке mex, о которой я писал выше. Укажите название пространство имен (я указал WcfServiceReference) и нажмите на кнопку Ок. Visual Studio создаст класс-обертку для методов службы, и вызвать метод станет проще простого:
WcfServiceReference.MyWcfServiceClient wcf =
new WcfHosting.WcfServiceReference.MyWcfServiceClient();
wcf.Open();
string date = wcf.GetDate();
wcf.Close();