使Windows服务将活动值发送回WCF客户端

本文关键字:WCF 客户端 Windows 服务 活动 | 更新日期: 2023-09-27 18:18:01

我有一个使用net的Windows服务。管道作为WCF服务器和Windows窗体客户端连接到它。有一个名为ConfigSettings的类,其中包含我希望客户端查询的值。

我想让客户端读取服务使用的serviceConfig实例中的当前值。最终,我希望客户端改变其中的值,但首先要循序渐进。

表单可以通过命名管道与服务器通信,但是` return serviceConfig `正在向客户端发送一个新的空实例。我需要服务正在积极使用的数据(即serviceConfig)。设置1 = x;serviceConfig。seting2 = "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之间的隔膜。

你将如何处理这个问题?

  • 需要Form进行配置的Windows服务。
  • 服务启动时,从磁盘加载一个config.xml文件。(一个序列化类)
  • 当GUI启动时,我想:
    • 检索当前配置,
    • 做一些修改,
    • 将其推回服务,
  • 触发服务重新读取和响应新的配置。

我试图避免静态/写入配置文件到磁盘,并告诉服务重新读取它。"看起来"WCF是可行的。

更新2 似乎只要将服务中的主配置更改为静态,WCF服务就可以直接访问它。我可以发誓我在发帖之前就这么做了,但我想没有。

我还将Service1的命名分离为上面的WCFService1,但事实证明这无关紧要,并且无论哪种方式都可以工作。

新的完整代码已在上面更新。

使Windows服务将活动值发送回WCF客户端

你正在混淆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()
        { }
    }
}

有几种方法可以做到这一点(在服务实例中注入依赖)。我将展示其中两个。

这里有一个特殊的情况,因为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)
  {
  }
  ...
}

我承认,自从我接触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服务一样运行,就像其他答案所建议的那样,是问题所在。但是我同意最好是把它们分成不同的类