Search code examples
c#visual-studioweb-serviceswcfself-hosting

Create a self hosted WCF Service inside a Windows Form


If I use this code for self Host a WCF service in a Console application it works. I run the host app and then from another app (which I call the client app,) I can add the service reference from visual studio > solution explorer > ADD SERVICE REFERENCE > http://10.131.131.14:8080/sendKioskMessage > click GO, add the service with no problems and consume it from the client app (which is a windows form)

But if I run the same code in a Windows Form, I run first the (SELF HOST WCF) windows form app, then from the other app (client app) in visual studio I try to add the service reference from ADD SERVICE REFERENCE in solution explorer (Just the same way that it works before but with the Console App self host) but it throws the following error:

*

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.

*

The IP that I use is the IP of my pc where both apps are running. I also used localhost instead of my actual IP with the same result.

Windows Form Code (can't add the service from another app):

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

Console App Code (Works! I can add the service from other client app):

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

I don't know why I can consume the service if the service is self hosted in a console app, but I can't add it if the service is self hosted in a Windows Form.

I will appreciate a lot your help to achieve this from a Windows From, since I need to self host the WCF service from a windows form, no a console app.

I'm using Visual Studio 2017, .Net Framework 4.6.1

THANKS IN ADVANCE GUYS!!


Solution


  • TL;DR the console app works because you have a delay before shutting down the service; the WinForms host doesn't


    The reason your console WCF host service works is that you start the hosting and continue until the Console.ReadLine() line:

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

    ...after which the service is torn down. Prior to that, your other clients can connect fine and add Service References.

    The WinForms app has no such delay:

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

    ...it is immediately shutdown when the code goes out of scope of the using block. The using will automatically call Dispose() on the host object which in turn calls Close().

    Consider placing the host into a variable like so:

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

    Later, you can close the _host instance with a call to Close.