Вторым способом размещения службы WCF является размещение этой службы в контексте приложения Windows. Такой способ размещения называетсяавторазмещением (self-hosting). При авторазмещении служба может быть запущена в контексте любого приложения, будь то консольное приложение, приложение WinForms, приложение WPF или сервис Windows.
Например, размещение службы как Windows-сервис дает возможность управлять запуском и остановкой этой службы, как через командную строку так и через оснастку Service Control Management, а так же настраивать возможные варианты действий при успешном или неудачном запуске. Более того, при нужной настройке служба будет автоматически запускаться при старте ОС. Если же служба будет размещена в Windows-приложении, то для ее старта необходимо будет так же запустить и само приложение (а так же нужно будет контролировать старт этого приложения при каждой перезагрузке системы). К тому же не всегда удобно, при входе на сервер видеть запущенные окна различных приложений, в контексте которых размещены службы WCF.Но с другой стороны, в зависимости от функций службы WCF, размещение внутри Windows-приложения может быть единственным вариантом. Примером таких служб могут быть службы, работающие как узлы пиринговой сети, либо службы постоянно прослушивающие определенный адрес, на который клиенты отправляют сообщения.
В этой статье помимо самого способа размещения службы WCF я остановлюсь более детально и на самом процессе ее создания (чего я не сделал в первой статье). Для примера я создам WCF службу, которая может быть запущена как сервис ОС или как консольное приложение.
Создадим в Visual Studio новое консольное приложение (New->Project->Console Application). Я назвал его WcfHosting.
Первым делом реализуем сам WCF-сервис внутри нашего консольного приложения:
1. Добавим в проект новый класс и назовем его WcfService.cs
В этом классе будет реализован процесс запуска и остановки нашей службы WCF. Так же здесь я реализую интерфейс методов (в терминологии WCF - контракт), которыми будет обладать наша служба, а так же их реализацию в производном классе.
Для того, чтобы начать работать с объектной моделью WCF нужно добавить в наш проект ссылку на сборку System.SeviceModel. Именно в пространстве имен этой сборки находятся методы создания и управления WCF-сервисами.
2. На втором шаге реализуем интерфейс методов (контракт) WCF-службы в файле WcfService.cs. В интерфейсе мы просто декларируем список всех методов, которыми будет обладать наша служба. Я назвал интерфейс IMyWcfService. В нем я определяю всего один метод GetDate(), который будет возвращать стоку текущей даты.
[ServiceContract] public interface IMyWcfService { [OperationContract] string GetDate(); }
Обратите внимание на атрибуты ServiceContract и OperationContract. WCFреализует свой функционал через контракты. Суть контракта WCF, как и суть обычного контракта из нашей повседневной жизни, заключается в определении, что будет делать наш сервис, какими возможностями он будет обладать. Для WCF контракт - это обычный интерфейс, в котором описаны все методы этой службы. Для того чтобы служба знала, что какой-либо интерфейс является контрактом, перед ним ставится атрибут ServiceContract (контракт о службе), а, соответственно, перед каждым его методом ставится атрибут OperationContract(контракт об операции).
Как только мы объявили контракт службы, осталось только его реализовать. Для этого нужно создать класс, производный от контракта и реализовать все его методы:
public class MyWcfService : IMyWcfService { #region IMyWcfService Members public string GetDate() { return DateTime.Now.ToString(); } #endregion }
3. Теперь все готово для реализации самой WCF-службы. В файле WcfService.cs создадим новый класс WcfService. Этот класс будет отвечать за создание, запуск и остановку службы WCF. Данный класс я реализовал на основе шаблона проектирования Singleton. Более подробно с шаблонами проектирования, можно ознакомиться по этой ссылке: http://msdn.microsoft.com/ru-ru/windows/gg543141.
Почему 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 - ее остановке:
Класс 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();