如何在WCF服务和宿主应用程序之间进行通信?

4
我通过这个教程创建了一个WCF服务。一切正常,没有问题。
现在我将服务托管到了一个托管应用程序中。但同时我想在主机应用程序中使用来自客户端的输入。
我不需要客户端和服务之间的双向通信,只需要服务与主机应用程序之间的通信。
如何处理这种情况最好?
3个回答

3

我通过这个问题中的信息解决了它。其中指出服务类也可以传递给主机。然后只需添加一个事件监听器,响应来自服务的事件即可。


在这种情况下,您的服务将是单例。 - Ladislav Mrnka

2
有一个框架和教程可用,似乎可以很好地处理这个问题 WPF Service Host on Codeplex
编辑:更新以说明由WPF Service Host模板创建的技术。
WPF Service Host是Visual Studio的一个模板,它创建了一个骨架应用程序和测试客户端。我将在这里描述相关部分。
这是骨架项目的样子: Skeleton Project ClientServiceHost.cs
using System;
using System.ServiceModel;

namespace WPFServiceHost1.Service
{
    public class ClientServiceHost : IDisposable
    {
        private bool _Initalized;

        private ServiceHost _InnerServiceHost;
        private MainWindow _MainWindow;

        public ClientServiceHost(MainWindow mainWindow)
        {
            _MainWindow = mainWindow;
            InitializeServiceHost();
        }

        private void InitializeServiceHost()
        {
            try
            {
                ClientService clientService = new ClientService(_MainWindow);
                _InnerServiceHost = new ServiceHost(clientService);

                _InnerServiceHost.Opened += new EventHandler(_InnerServiceHost_Opened);
                _InnerServiceHost.Faulted += new EventHandler(_InnerServiceHost_Faulted);
                _InnerServiceHost.Open();
            }
            catch (Exception ex)
            {
                throw new Exception("Unable to initialize ClientServiceHost", ex);
            }
        }

        void _InnerServiceHost_Opened(object sender, EventArgs e)
        {
            _Initalized = true;
        }

        void _InnerServiceHost_Faulted(object sender, EventArgs e)
        {
            this._InnerServiceHost.Abort();

            if (_Initalized)
            {
                _Initalized = false;
                InitializeServiceHost();
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            try
            {
                _InnerServiceHost.Opened -= _InnerServiceHost_Opened;
                _InnerServiceHost.Faulted -= _InnerServiceHost_Faulted;
                _InnerServiceHost.Close();
            }
            catch
            {
                try { _InnerServiceHost.Abort(); }
                catch { }
            }
        }
        #endregion
    }
}

MainWindow.xaml

<Window x:Class="WPFServiceHost1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPFServiceHost1" Height="344" Width="343" Closing="Window_Closing">
    <Grid>
        <Label Height="28" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="119">Messages received:</Label>
        <ListBox Margin="21,38,26,21" Name="listBox1" />
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Threading;
using WPFServiceHost1.Service;

namespace WPFServiceHost1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public SynchronizationContext _SyncContext = SynchronizationContext.Current;
        private ClientServiceHost _ClientServiceHost;

        public MainWindow()
        {
            InitializeComponent();
            _ClientServiceHost = new ClientServiceHost(this);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            _ClientServiceHost.Dispose();
        }
    }
}

IClientService.cs

using System.ServiceModel;

namespace WPFServiceHost1.Service
{
    [ServiceContract(Namespace = "urn:WPFServiceHost")]
    public interface IClientService
    {
        [OperationContract]
        void ClientNotification(string message);
    }
}

ClientService.cs

using System;
using System.ServiceModel;

namespace WPFServiceHost1.Service
{
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = false)]
    public class ClientService : IClientService
    {
        private MainWindow _MainWindow;

        public ClientService(MainWindow window)
        {
            _MainWindow = window;
        }

        #region IClientService Members
        public void ClientNotification(string message)
        {
            try
            {
                _MainWindow._SyncContext.Send(state =>
                {
                    _MainWindow.listBox1.Items.Add(message);
                }, null);
            }
            catch (Exception ex)
            {
                throw new FaultException(ex.Message);
            }
        }
        #endregion
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="ClientServiceBehavior">
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="ClientServiceBehavior"
                name="WPFServiceHost1.Service.ClientService">
                <endpoint address="ClientService" binding="basicHttpBinding" contract="WPFServiceHost1.Service.IClientService"/>
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8010" />
                    </baseAddresses>
                </host>
            </service>
        </services>
    </system.serviceModel>
</configuration>

TestClient和Program.cs:

using System;
using System.ServiceModel;
using System.Threading;

namespace TestClient
{
    class Program
    {
        static void Main(string[] args)
        {
            IClientService proxy = null;

            try
            {
                proxy = ChannelFactory<IClientService>.CreateChannel(new BasicHttpBinding(), new EndpointAddress("http://localhost:8010/ClientService"));
                Console.WriteLine("Press <Enter> when ClientService is running.");
                Console.ReadLine();
                Console.WriteLine();

                Console.WriteLine("Sending a single message to ClientService");
                proxy.ClientNotification("Hello from TestClient");

                Console.WriteLine();

                Console.Write("Enter a valid number to load test ClientService: ");
                string result = Console.ReadLine();
                int testCount = Convert.ToInt32(result);
                int counter = 0;
                object counterLock = new object();

                while (true)
                {
                    lock (counterLock)
                    {
                        Thread t = new Thread(() => proxy.ClientNotification(string.Format("Load test from TestClient: {0}", ++counter)));
                        t.Start();
                    }

                    if (counter == testCount)
                        break;
                }

                Console.ReadLine();
            }
            finally
            {
                ICommunicationObject co = proxy as ICommunicationObject;
                try
                {
                    co.Close();
                }
                catch { co.Abort(); }
            }

            Console.ReadLine();
        }
    }
}

2

这就像线程之间的通信。您需要一些共享变量和适当的锁定/同步。您的主机应用程序将写入此变量,您的服务将能够从该变量读取。


谢谢您的建议。好的,我觉得这可能行得通。但是如何从主机应用程序或反过来访问服务对象的变量?由于我使用Host.open()命令托管服务,所以我没有对服务的引用。 - Martijn
你不能在服务实例中访问变量。你的变量必须由公共对象共享。例如,你可以实现单例模式,将这些变量暴露给服务和主机。另一种方法是使用服务定位器来共享对象。 - Ladislav Mrnka
你的意思是我需要在主机应用程序中创建一个具有单例属性的单独对象。我如何从我的服务中创建对此对象的引用?我的最终目标是在WCF服务从客户端接收数据时触发主机应用程序中的函数,使用单例方法可以实现吗? - Martijn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接