Получение службы Windows для отправки активных значений обратно клиенту WCF
У меня есть служба Windows, которая использует net.pipe в качестве сервера WCF и клиент форм Windows для подключения к нему. Существует класс ConfigSettings, в котором есть значения, которые я хочу, чтобы клиент запросил.
Я хочу, чтобы клиент прочитал текущие значения внутри экземпляра serviceConfig, который использует служба. В конечном счете, я хочу, чтобы клиент изменил значения в нем, но ребенок делает первые шаги.
Форма может общаться с сервером через именованные каналы, но 'return serviceConfig отправляет новый пустой экземпляр обратно клиенту. Мне нужны данные, которые служба активно использует (то есть serviceConfig.Setting1 = x; serviceConfig.Setting2 = "foo";)
Код службы Windows и сервера WCF (обновлен до рабочей версии):
using System.IO;
using System.ServiceModel;
using System.ServiceProcess;
namespace WindowsServiceTest
{
public partial class Service1 : ServiceBase
{
internal static ServiceHost myServiceHost = null;
//this is the master config that the service uses
public static ConfigSettings serviceConfig = new ConfigSettings();
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(WCFService1));
myServiceHost.Open();
//set active default settings
serviceConfig.Setting1 = 1;
serviceConfig.Setting2 = "initial.Setting2:" + serviceConfig.Setting1;
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
public partial class WCFService1 : IService1
{
public ConfigSettings GetConfig()
{
return Service1.serviceConfig;
}
public void SetConfig(ConfigSettings sentConfig)
{
Service1.serviceConfig = sentConfig;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ConfigSettings GetConfig();
[OperationContract]
void SetConfig(ConfigSettings sentConfig);
}
public class ConfigSettings
{
public int Setting1 { get; set; }
public string Setting2 { get; set; }
public ConfigSettings() { }
}
}
Клиент получает конфигурацию следующим образом (обновлено с некоторыми изменениями):
using System;
using System.ServiceProcess;
using System.Windows.Forms;
using WindowsServiceTest;
namespace WindowsServiceTestForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
ConfigSettings config = new ConfigSettings();
//GetConfig()
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "Service1");//this will grant permission to access the Service
//get the current config and display
config = myService.GetConfig();
MessageBox.Show(config.Setting1 + "\r\n" + config.Setting2, "config");
myService.Close();
}
//SetConfig(ConfigSettings)
private void button2_Click(object sender, EventArgs e)
{ //make changes to values
config.Setting1 += 1;
config.Setting2 = "new.Setting2:" + config.Setting1;
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "Service1");//this will grant permission to access the Service
//send the new config
myService.SetConfig(config);
myService.Close();
}
}
}
Обновление: Возможно, то, что я думаю, должно быть сделано, является излишним. Кажется, что я попал в мембрану между WCF и Windows Service.
Как бы вы подошли к этой проблеме?
- Служба Windows, которая нуждается в форме для конфигурации.
- Когда служба запускается, она загружает файл config.xml с диска. (сериализованный класс)
- Когда GUI запускается, я хочу:
- восстановить его текущую конфигурацию,
- внести в него некоторые изменения,
- подтолкнуть его обратно в сервис,
- вызвать службу перечитать и отреагировать на новую конфигурацию.
Я пытался статически избегать / записывать файл конфигурации на диск и предлагать сервису перечитать его снова. Казалось, что WCF - это путь.
Обновление 2 Похоже, что просто изменив основной конфиг в службе на статический, сервис WCF может получить к нему прямой доступ. Я мог бы поклясться, что сделал это изначально, прежде чем опубликовать, но я думаю, что нет.
Я также разделил именование Service1 на WCFService1 выше, но оказалось, что это не имеет значения и работает в любом случае.
Новый полный код был обновлен выше.
3 ответа
Вы запутались между службой Windows и службой WCF - и попытались поместить их обоих в один и тот же класс, - хотя это возможно - это будет легче понять, если разделить их на два класса.
В вашем примере служба Windows запускается, создает новый экземпляр себя в качестве службы WCF, а затем устанавливает элементы конфигурации в экземпляре службы Windows, то есть конфигурация пуста в экземпляре службы WCF.
попробуйте это вместо
using System.ServiceModel;
using System.ServiceProcess;
namespace WindowsServiceTest
{
public partial class Service1 : ServiceBase
{
internal static ServiceHost myServiceHost = null;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(WCFService1 ));
myServiceHost.Open();
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
public class WCFService1 : IService1
{
public WCFService1()
{
//change master settings from null
myConfig.Setting1 = "123";
myConfig.Setting2 = "456";
}
//this is the master config that the service uses
public ConfigSettings myConfig = new ConfigSettings();
public ConfigSettings GetConfig()
{
return myConfig;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ConfigSettings GetConfig();
}
public class ConfigSettings
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
public ConfigSettings()
{ }
}
}
Я признаю, что прошло много времени с тех пор, как я коснулся WCF, но мне кажется, что ты ConfigSettings
В классе отсутствуют некоторые атрибуты, необходимые для сериализации через WCF.
using System.Runtime.Serialization;
[DataContract]
public class ConfigSettings
{
[DataMember]
public string Setting1 { get; set; }
[DataMember]
public string Setting2 { get; set; }
public ConfigSettings()
{ }
}
Я не верю, что проблема заключается в том, чтобы ваша служба Windows работала как служба WCF, как предлагали другие ответы. Но я согласен, что лучше, чтобы они были отдельными классами.
Есть несколько способов сделать это (внедрить зависимости в экземпляр службы). Я покажу два из них.
У вас есть особый случай, потому что служба Windows также является вашей реализацией службы WCF. Я думаю, что достаточно скоро вы захотите разделить их. Итак, мои примеры основаны на отдельной реализации сервиса WCF.
Дело в том, что в WCF есть концепция режима экземпляра службы. По умолчанию это PerCall
Это означает, что новый Service1
экземпляр создается для обработки каждого запроса. Это делает более трудным введение чего-либо в этих случаях.
Самый простой способ - установить режим экземпляра на Single
это означает, что будет только один Service1
экземпляр обрабатывает все запросы.
Второе легко реализовать:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service1Implementation : IService1
{
private ConfigSettings _configSettings;
public Service1(ConfigSettings settings)
{
//now you have the settings in the service
_configSettings = setting;
}
...
}
//in the Windows Service
myServiceHost = new ServiceHost(typeof(Service1Implementation), new Service1Implementation(myConfig));
myServiceHost.Open();
Первое решение включает в себя создание и указание фабрики экземпляров сервисов, которую инфраструктура будет использовать позже для создания экземпляров сервисов за вызов.
Фабрика дает вам возможность создать экземпляр Service1
сами и передайте конфиг.
Для этого нужно написать довольно много кода, но я покажу самое важное. Для полного решения прочитайте это.
Для вас проще сделать Windows Service просто реализовать IInstanceProvider
:
public class Service1 : ServiceBase, IInstanceProvider
{
private ConfigSettings _myConfig; //assign this member later on
...
//IInstanceProvider implementation
public object GetInstance(InstanceContext instanceContext, Message message)
{
//this is how you inject the config
return new Service1Implementation(_myConfig);
}
public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
...
}