在 Windows 表单中创建自托管 WCF 服务

Create a self hosted WCF Service inside a Windows Form

如果我使用此代码在控制台应用程序中自行托管 WCF 服务,它就可以工作。我 运行 主机应用程序,然后从另一个应用程序(我称之为客户端应用程序),我可以从 visual studio 添加服务参考 > 解决方案资源管理器 > 添加服务参考 > http://10.131.131.14:8080/sendKioskMessage > 单击开始,毫无问题地添加服务并从客户端应用程序使用它(这是一个 windows 表单)

但是如果我 运行 在 Windows 表单中使用相同的代码,我 运行 首先是 (SELF HOST WCF) windows 表单应用程序,然后是另一个visual studio 中的应用程序(客户端应用程序)我尝试在解决方案资源管理器中添加来自 ADD SERVICE REFERENCE 的服务引用(与之前的工作方式相同,但使用控制台应用程序自托管)但它抛出以下错误:

*

An error (Details) occurred while attempting to find services at http://10.131.131.14:8080/sendKioskMessage.

(If I click Details Link, says the following:)

There was an error downloading 'http://10.131.131.14:8080/sendKioskMessage/$metadata'. Unable to connect to the remote server. Metadata contains a reference that cannot be resolved: 'http://10.131.131.14:8080/sendKioskMessage'. There was no endpoint listening at http://10.131.131.14:8080/sendKioskMessage that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. Unable to connect to the remote server. If the service is defined in the current solution, try building the solution and adding the service reference again.

*

我使用的 IP 是我电脑的 IP,两个应用程序都在 运行ning。我也使用 localhost 而不是我的实际 IP,结果相同。

Windows 表单代码(无法从其他应用添加服务):

public partial class KioskosServerForm : Form
    {


        [ServiceContract]
        public interface IKioskMessageService
        {
            [OperationContract]
            string SendKioskMessage(string message);
        }

        public class KioskMessageService : IKioskMessageService
        {
            public string SendKioskMessage(string message)
            {
                return string.Format("Message sent: {0}", message);
            }
        }

        public KioskosServerForm()
        {
            InitializeComponent();
        }

        private void KioskosServerForm_Load(object sender, EventArgs e)
        {
            Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");

            try
            {
                // Create the ServiceHost.
                using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
                {
                    // Enable metadata publishing.
                    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                    smb.HttpGetEnabled = true;
                    smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
                    host.Description.Behaviors.Add(smb);

                    // Open the ServiceHost to start listening for messages. Since
                    // no endpoints are explicitly configured, the runtime will create
                    // one endpoint per base address for each service contract implemented
                    // by the service.
                    host.Open();
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.InnerException.Message);
            }
        }
    }

控制台应用程序代码(有效!我可以从其他客户端应用程序添加服务):

[ServiceContract]
public interface IKioskMessageService
{
    [OperationContract]
    string SendKioskMessage(string message);
}

public class KioskMessageService : IKioskMessageService
{
    public string SendKioskMessage(string message)
    {
        return string.Format("Message sent: {0}", message);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Uri baseAddress = new Uri("http://localhost:8080/sendKioskMessage");

        // Create the ServiceHost.
        using (ServiceHost host = new ServiceHost(typeof(KioskMessageService),baseAddress))
        {
            // Enable metadata publishing.
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            host.Description.Behaviors.Add(smb);

            // Open the ServiceHost to start listening for messages. Since
            // no endpoints are explicitly configured, the runtime will create
            // one endpoint per base address for each service contract implemented
            // by the service.
            host.Open();

            Console.WriteLine("The service is ready at {0}", baseAddress);
            Console.WriteLine("Press <Enter> to stop the service.");
            Console.ReadLine();

            // Close the ServiceHost.
            host.Close();
        }
    }
}

如果服务在控制台应用程序中自托管,我不知道为什么我可以使用该服务,但如果服务在 Windows 表单中自托管,我就无法添加它。

非常感谢您通过 Windows 实现这一目标的帮助,因为我需要从 windows 表单自行托管 WCF 服务,而不是控制台应用程序。

我正在使用 Visual Studio 2017,.Net Framework 4.6.1

提前谢谢大家!!


TL;DR 控制台应用程序可以运行,因为您在关闭服务之前有延迟; WinForms 主机没有


您的控制台 WCF 主机服务工作的原因是您开始托管并继续直到 Console.ReadLine() 行:

host.Open();

Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();  // <-------- program waits here

// Close the ServiceHost.
host.Close();

...之后服务被拆除。在此之前,您的其他客户端可以正常连接并添加服务引用

WinForms 应用没有这样的延迟:

private void KioskosServerForm_Load(object sender, EventArgs e)
{
    Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");

    try
    {
        // Create the ServiceHost.
        using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
        {
            // Enable metadata publishing.
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
                host.Description.Behaviors.Add(smb);

            // Open the ServiceHost to start listening for messages. Since
            // no endpoints are explicitly configured, the runtime will create
            // one endpoint per base address for each service contract implemented
            // by the service.
            host.Open();  // <------ opened here
        } // <------ shutdown here
     } 
     catch (Exception exp)
     {
         MessageBox.Show(exp.InnerException.Message);
     }
}

...当代码超出 using 块的范围时,它会立即关闭。 using 将自动调用 host 对象上的 Dispose(),后者又调用 Close()

考虑将主机放入变量中,如下所示:

ServiceHost _host;  // <---------- new!

private void KioskosServerForm_Load(object sender, EventArgs e)
{
    Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");

    try
    {
        // Create the ServiceHost.
        _host = new ServiceHost(typeof(KioskMessageService), baseAddress))
        // Enable metadata publishing.
        ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
        smb.HttpGetEnabled = true;
        smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
       _host.Description.Behaviors.Add(smb);

        // Open the ServiceHost to start listening for messages. Since
        // no endpoints are explicitly configured, the runtime will create
        // one endpoint per base address for each service contract implemented
        // by the service.
        _host.Open();
     }
     catch (Exception exp)
     {
         MessageBox.Show(exp.InnerException.Message);
     }
 }

稍后,您可以通过调用 Close.

关闭 _host 实例