Search code examples
c#iisiis-expressnettcpbindingservicehost

Calling WCF Service using NetTcpBinding from the same winform


Hey all I have the following code that allows me to start a temp web service for WCF services without spooling up IIS Express myself.

However, the code below works just fine if I have the fetching of the data in another winform. Copying the same code over to the WCF service winform results in it freezing when trying to send patient index number.

The class code (Class1.cs):

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;

/*
    HOW TO HOST THE WCF SERVICE IN THIS LIBRARY IN ANOTHER PROJECT
    You will need to do the following things: 
    1)    Add a Host project to your solution
        a.    Right click on your solution
        b.    Select Add
        c.    Select New Project
        d.    Choose an appropriate Host project type (e.g. Console Application)
    2)    Add a new source file to your Host project
        a.    Right click on your Host project
        b.    Select Add
        c.    Select New Item
        d.    Select "Code File"
    3)    Paste the contents of the "MyServiceHost" class below into the new Code File
    4)    Add an "Application Configuration File" to your Host project
        a.    Right click on your Host project
        b.    Select Add
        c.    Select New Item
        d.    Select "Application Configuration File"
    5)    Paste the contents of the App.Config below that defines your service endoints into the new Config File
    6)    Add the code that will host, start and stop the service
        a.    Call MyServiceHost.StartService() to start the service and MyServiceHost.EndService() to end the service
    7)    Add a Reference to System.ServiceModel.dll
        a.    Right click on your Host Project
        b.    Select "Add Reference"
        c.    Select "System.ServiceModel.dll"
    8)    Add a Reference from your Host project to your Service Library project
        a.    Right click on your Host Project
        b.    Select "Add Reference"
        c.    Select the "Projects" tab
    9)    Set the Host project as the "StartUp" project for the solution
        a.    Right click on your Host Project
        b.    Select "Set as StartUp Project"

    ################# START MyServiceHost.cs #################

    using System;
    using System.ServiceModel;

    // A WCF service consists of a contract (defined below), 
    // a class which implements that interface, and configuration 
    // entries that specify behaviors and endpoints associated with 
    // that implementation (see <system.serviceModel> in your application
    // configuration file).

    internal class MyServiceHost
    {
        internal static ServiceHost myServiceHost = null;

        internal static void StartService()
        {
            //Consider putting the baseAddress in the configuration system
            //and getting it here with AppSettings
            Uri baseAddress = new Uri("http://localhost:8080/service1");

            //Instantiate new ServiceHost 
            myServiceHost = new ServiceHost(typeof(TestService.service1), baseAddress);

            //Open myServiceHost
            myServiceHost.Open();
        }

        internal static void StopService()
        {
            //Call StopService from your shutdown logic (i.e. dispose method)
            if (myServiceHost.State != CommunicationState.Closed)
                myServiceHost.Close();
        }
    }

    ################# END MyServiceHost.cs #################
    ################# START App.config or Web.config #################

    <system.serviceModel>
    <services>
         <service name="TestService.service1">
           <endpoint contract="TestService.IService1" binding="wsHttpBinding"/>
         </service>
       </services>
    </system.serviceModel>

    ################# END App.config or Web.config #################

*/
namespace TestService
{
    // You have created a class library to define and implement your WCF service.
    // You will need to add a reference to this library from another project and add 
    // the code to that project to host the service as described below.  Another way
    // to create and host a WCF service is by using the Add New Item, WCF Service 
    // template within an existing project such as a Console Application or a Windows 
    // Application.

    [ServiceContract()]
    public interface IService1
    {
        [OperationContract]
        Patient GetPatient(Int32 index);

        [OperationContract]
        void SetPatient(Int32 index, Patient patient);
    }

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class PatientService : IService1
    {
        Patient[] pat = null;

        public PatientService()
        {
            pat = new Patient[3];

            pat[0] = new Patient();
            pat[0].FirstName = "Bob";
            pat[0].LastName = "Chandler";

            pat[1] = new Patient();
            pat[1].FirstName = "Joe";
            pat[1].LastName = "Klink";

            pat[2] = new Patient();
            pat[2].FirstName = "Sally";
            pat[2].LastName = "Wilson";
        }

        public Patient GetPatient(Int32 index)
        {
            if (index <= pat.GetUpperBound(0) && index > -1)
                return pat[index];
            else
                return new Patient();
        }

        public void SetPatient(Int32 index, Patient patient)
        {
            if (index <= pat.GetUpperBound(0) && index > -1)
                pat[index] = patient;
        }
    }

    [DataContract]
    public class Patient
    {
        string firstName;
        string lastName;

        [DataMember]
        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        [DataMember]
        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }
    }
}

the Service Code (Form1.cs):

using System;
using System.Windows.Forms;
using System.ServiceModel;

namespace TestService
{
    public partial class Form1 : Form
    {
        bool serviceStarted = false;
        ServiceHost myServiceHost = null;
        NetTcpBinding binding;
        Uri baseAddress = new Uri("net.tcp://localhost:2202/PatientService");

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (serviceStarted)
            {
                myServiceHost.Close();
                serviceStarted = false;
                button1.Text = "Start Service";
            }
            else
            {
                binding = new NetTcpBinding();
                myServiceHost = new ServiceHost(typeof(PatientService), baseAddress);
                myServiceHost.AddServiceEndpoint(typeof(IService1), binding, baseAddress);

                myServiceHost.Open();

                serviceStarted = true;
                button1.Text = "Stop Service";    
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            IService1 patientSvc = null;
            EndpointAddress address = new EndpointAddress(baseAddress);
            ChannelFactory<IService1> factory = new ChannelFactory<IService1>(binding, address);
            patientSvc = factory.CreateChannel();

            Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));

            if (patient != null)
            {
                textBox2.Text = patient.FirstName;
                textBox3.Text = patient.LastName;
            }
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

enter image description here

The code line here:

Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));

is where it freezes up. Again, this is the same code that is in the other winform and it works. It's just when using it inside the service itself doesn't seem to work the same way for some reason??

The error it gives is this:

System.TimeoutException: 'This request operation sent to net.tcp://localhost:2202/PatientService did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.'

Anyone have an idea on how to get it to work?

This is the other winform code:

using System;
using System.Windows.Forms;
using System.ServiceModel;
using TestService;

namespace TestClient
{
    public partial class Form1 : Form
    {
        IService1 patientSvc = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            EndpointAddress address = new EndpointAddress(new Uri("net.tcp://localhost:2202/PatientService"));
            NetTcpBinding binding = new NetTcpBinding();
            ChannelFactory<IService1> factory = new ChannelFactory<IService1>(binding, address);
            patientSvc = factory.CreateChannel();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));

            if (patient != null)
            {
                textBox2.Text = patient.FirstName;
                textBox3.Text = patient.LastName;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Patient patient = new Patient();
            patient.FirstName = textBox2.Text;
            patient.LastName = textBox3.Text;

            patientSvc.SetPatient(Convert.ToInt32(textBox1.Text), patient);
        }
    }
}

TestClient code (Program.cs):

using System;
using System.Windows.Forms;

namespace TestClient
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

enter image description here


Solution

  • Got it! Thank to @Eser for the hint :)

    private void button3_Click(object sender, EventArgs e)
    {
        IService1 patientSvc = null;
        EndpointAddress address = new EndpointAddress(baseAddress);
        ChannelFactory<IService1> factory = new ChannelFactory<IService1>(binding, address);
        patientSvc = factory.CreateChannel();
    
        Thread thread = new Thread(() => sendData(patientSvc));
        thread.Start();            
    }
    
    delegate void SetTextCallback(string text, bool isTxt2);
    
    private void SetText(string text, bool isTxt2)
    {
        if (isTxt2)
        {
            if (this.textBox2.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                textBox2.Invoke(d, new object[] { text, isTxt2 });
            }
            else
            {
                textBox2.Text = text;
            }
        } else {
            if (this.textBox3.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                textBox2.Invoke(d, new object[] { text, isTxt2 });
            } else {
                this.textBox3.Text = text;
            }
        }
    }
    
    public void sendData(IService1 patientSvc)
    {
        Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));
    
        if (patient != null)
        {
            SetText(patient.FirstName, true);
            SetText(patient.LastName, false);
        }
    }