Клиент WCF зависает при вызове метода из Сервиса - PullRequest
3 голосов
/ 01 июня 2011

У меня странная проблема, когда мой клиент зависает при вызове метода из моей службы WCF. Теперь действительно странно то, что этого не происходит, когда клиент является консольным приложением. Это происходит, когда клиент является приложением WinForm или WPF.

Я создал клиентскую библиотеку, которую клиент WCF может использовать для подключения к службе, см. Здесь:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;  //needed for WCF communication

namespace DCC_Client
{
    public class DCCClient
    {
        private DuplexChannelFactory<ServiceReference1.IDCCService> dualFactory;

        public ServiceReference1.IDCCService Proxy;

        public DCCClient()
        {
            //Setup the duplex channel to the service...
            NetNamedPipeBinding binding = new NetNamedPipeBinding();
            dualFactory = new DuplexChannelFactory<ServiceReference1.IDCCService>(new Callbacks(), binding, new EndpointAddress("net.pipe://localhost/DCCService"));
        }

        public void Open()
        {
            Proxy = dualFactory.CreateChannel();
        }

        public void Close()
        {
            dualFactory.Close();
        }
    }

    public class Callbacks : ServiceReference1.IDCCServiceCallback
    {
        void ServiceReference1.IDCCServiceCallback.OnCallback(string id, string message, Guid key)
        {
            Console.WriteLine(string.Format("{0}: {1}", id, message));
        }
    }
}

Вот код для рабочего Консольного клиента WCF:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using DCC_Client;

namespace Client_Console_Test
{
    class Program
    {
        private static DCCClient DCCClient;

        static void Main(string[] args)
        {
            try
            {
                DCCClient = new DCCClient();

                DCCClient.Open();

                DCCClient.Proxy.DCCInitialize(); //returns fine from here

                Console.ReadLine();
                DCCClient.Proxy.DCCUninitialize();

                DCCClient.Close();
            }
            catch (Exception e)
            {
                throw;
            }
        }
    }
}

А вот код для клиента WPF, который зависает (см. Комментарий)

using System; //etc

using DCC_Client;  //Used for connection to DCC Service

namespace Client_WPF_Test
{
    public partial class Main : Window
    {
        private static DCCClient DCCClient;

        public Main()
        {
            InitializeComponent();

            DCCClient = new DCCClient();

            DCCClient.Open();
        }

        private void Connect_btn_event() {

            try
            {
                DCCClient.Proxy.DCCInitialize(); //**never returns from this**
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

Я вошел в код DCCClient.Proxy.DCCInitialize();, и служба успешно выполняет команды, однако по какой-то причине клиент застревает здесь и не продолжает выполнение. Клиент не дает никаких исключений, и трассировка стека говорит [внешний код].

При этом Консольный клиент работает отлично. Я думаю, что мне здесь не хватает чего-то простого. Я ценю любую помощь, которую вы можете оказать.

1 Ответ

14 голосов
/ 01 июня 2011

В случае, если ваш сервисный вызов возвращает клиента напрямую с DCCInitialize, и как операция, так и операция обратного вызова не помечены как односторонние, ваше приложение будет заблокировано.Попробуйте пометить реализацию обратного вызова этим атрибутом:

[CallbackBehavior(ConcurrencyMode=ConcurrencyModel.Reentrant)]

Вместо этого вы можете также попытаться пометить операции в обоих контрактах с помощью

[OperationContract(IsOneWay=true)]

Но обе операции должны вернуть void

В последнем случае, если ни один из этих способов не помогает пометить вашу реализацию обратного вызова с помощью:

[CallbackBehavior(UseSynchronizationContext=false)]

, но в этом случае ваша операция обратного вызова будет выполняться в другом потоке, и она не сможет манипулироватьнепосредственно с элементами управления пользовательского интерфейса.

Редактировать:

WCF ведет себя по-разному, когда размещается в потоке пользовательского интерфейса.В таком сценарии все запросы обрабатываются в последовательном порядке в стандартном цикле сообщений Windows, поэтому, если вы вызываете службу, вы заблокировали текущий поток, но служба перезванивает вашему клиенту и ожидает обработки сообщения, но не может, потому что поток заблокированначальный вызов = тупик, пока не истечет начальный запрос.Используя последнее упомянутое поведение, вы скажете WCF не присоединяться к циклу сообщений Windows и вместо этого обрабатывать сообщения в отдельных потоках, как обычно.В этом нет проблемы безопасности, за исключением того факта, что вы не можете получить доступ к управлению пользовательским интерфейсом из методов, запущенных в других потоках - и WinForms, и WPF имеют подходы для передачи команд из другого потока.

...