-1

我有一个 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 服务之间遇到了障碍。

将如何解决这个问题?

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

我试图避免静态/将配置文件写入磁盘并告诉服务再次重新读取它。它“似乎”像 WCF 是要走的路。

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

我还将 Service1 的命名与上面的 WCFService1 分开,但事实证明这并不重要,并且可以使用任何一种方式。

上面更新了新的完整代码。

4

3 回答 3

1

您对 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()
        { }
    }
}
于 2014-08-14T08:28:51.317 回答
0

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

这里有一个特殊情况,因为 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 服务实现起来更容易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)
  {
  }
  ...
}
于 2014-08-14T08:48:29.177 回答
0

我承认我接触 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 服务运行是问题所在。但我确实同意最好让它们成为单独的类。

于 2014-08-14T13:55:48.170 回答