从同一个 winform 使用 NetTcpBinding 调用 WCF 服务

Calling WCF Service using NetTcpBinding from the same winform

嘿,我有以下代码,允许我为 WCF 服务启动临时 Web 服务,而无需自己假脱机 IIS Express。

但是,下面的代码工作正常 if 我在另一个 winform 中获取数据。将相同的代码复制到 WCF 服务 winform 会导致它在尝试发送患者索引号时冻结。

class代码(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; }
        }
    }
}

服务代码(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());
        }
    }
}

这里的代码行:

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

是它冻结的地方。同样,这与另一个 winform 中的代码相同,有效。只是当在服务内部使用它时,由于某种原因似乎无法以相同的方式工作??

它给出的错误是这样的:

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.'

有人知道如何让它工作吗?

这是另一个winform代码:

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);
        }
    }
}

测试客户端代码(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());
        }
    }
}

知道了!感谢@Eser 的提示 :)

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);
    }
}