Получение службы 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)
  {
  }
  ...
}
Другие вопросы по тегам