How to make a WCF interface utilize variables decl

2019-06-09 05:20发布

问题:

I recently followed the MSDN tutorial HERE In order to make a service with WCF. My concern now is how to access a variable declared in CalculatorWindowService From CalculatorService so that I can modify it's value for later use in CalculatorWindowService For example if in the methods for Add,Subtract,Divide, and Multiply if I Also wanted the results to be stored into the List<double> DoubleList declared in CalculatorWindowService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;
using System.ServiceModel;
using System.ServiceProcess;
using System.Configuration;
using System.Configuration.Install;

namespace Microsoft.ServiceModel.Samples
{
    // Define a service contract.
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
    }

    // Implement the ICalculator service contract in a service class.
    public class CalculatorService : ICalculator
    {
        // Implement the ICalculator methods.
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            return result;
        }

        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            return result;
        }

        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            return result;
        }

        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            return result;            
        }
    }

    public class CalculatorWindowsService : ServiceBase
    {
        public List<double> DoubleList = new List<int>();
        public ServiceHost serviceHost = null;
        public CalculatorWindowsService()
        {
            // Name the Windows Service
            ServiceName = "WCFWindowsServiceSample";
        }

        public static void Main()
        {
            ServiceBase.Run(new CalculatorWindowsService());
        }

        // Start the Windows service.
        protected override void OnStart(string[] args)
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
            }

            // Create a ServiceHost for the CalculatorService type and 
            // provide the base address.
            serviceHost = new ServiceHost(typeof(CalculatorService));

            // Open the ServiceHostBase to create listeners and start 
            // listening for messages.
            serviceHost.Open();
        }

        protected override void OnStop()
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
                serviceHost = null;
            }
        }
    }

    // Provide the ProjectInstaller class which allows 
    // the service to be installed by the Installutil.exe tool
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;

        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFWindowsServiceSample";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
}

回答1:

In order for your GUI (client) application to be able to talk to the Windows service, you need to use an Inter-Process Communication mechanism, since both the Windows service and the GUI are standalone, independent processes. The easiest way is to create a NetNamedPipe using Windows Communication Foundation.

Part I: Windows Service

I'm assuming you already have created a Windows service and know how to deploy it. So we begin by adding a contract to the Windows service project called ICalculatorService (make sure this interface is in its own file):

[ServiceContract]
public interface ICalculatorService
{
    [OperationContract]
    void Add(double value);

    [OperationContract]
    List<double> GetAllNumbers();
}

Then, we need to implement this interface. Create a new class (again, in its own file) called CalculatorService:

public class CalculatorService: ICalculatorService
{
    private static List<double> m_myValues = new List<double>();

    public void Add(double value)
    {
        m_myValues.Add(value);
    }

    public List<double> GetAllNumbers()
    {
        return m_myValues;
    }
}

Notice that we have a static List<double> which holds all the values. Now, we need make the Windows service a host, so that the client (GUI) can connect to it. The code-behind for the Windows service looks like this:

partial class CalculatorWindowsService : ServiceBase
{
    public ServiceHost m_host;

    public CalculatorWindowsService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        // If the host is still open, close it first.
        if (m_host != null)
        {
            m_host.Close();
        }

        // Create a new host
        m_host = new ServiceHost(typeof(CalculatorService), new Uri("net.pipe://localhost"));

        // Note: "MyServiceAddress" is an arbitrary name. You can change it to whatever you want.
        m_host.AddServiceEndpoint(typeof(ICalculatorService), new NetNamedPipeBinding(), "MyServiceAddress");

        m_host.Open();
    }

    protected override void OnStop()
    {
        // Close the host when the service stops
        if (m_host != null)
        {
            m_host.Close();
            m_host = null;
        }
    }
}

This is all we need for the on the Windows service side.

Part II: GUI (Client) First, we need to make sure we create a contract just like the one we created in the Windows service, so that the client can successfully make the service calls. To do this, simply copy the ICalculatorService interface to the the client application. Make sure you change the namespace so that it's consistent with other classes in the client. I created a simple Windows Forms program with two buttons: Add Value and Get All Values. The code behind for this form looks like this:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
    }

    private void AddValue_Click(object sender, EventArgs e)
    {
        using (ChannelFactory<ICalculatorService> facotry = new ChannelFactory<ICalculatorService>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/MyServiceAddress")))
        {
            ICalculatorService proxy = facotry.CreateChannel();

            // Generate a random number to send to the service
            Random rand = new Random();
            var value = rand.Next(3, 20);

            // Send the random value to Windows service
            proxy.Add(value);
        }
    }

    private void GetAllValues_Click(object sender, EventArgs e)
    {
        using (ChannelFactory<ICalculatorService> facotry = new ChannelFactory<ICalculatorService>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/MyServiceAddress")))
        {
            ICalculatorService proxy = facotry.CreateChannel();

            // Get all the numbers from the service
            var returnedResult = proxy.GetAllNumbers();

            // Display the values returned by the service
            MessageBox.Show(string.Join("\n", returnedResult));
        }
    }
}

Here's a snapshot of my files in the Windows service and WinForms projects to help you see where things are:

Edit You can change the m_myValues to public, and access it inside the CalculatorWindowsService class.