Клиент WCF зависает при вызове метода из службы


У меня есть эта странная проблема, когда мой клиент зависает, когда он вызывает метод из моей службы 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 3

1 ответ:

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

[CallbackBehavior(ConcurrencyMode=ConcurrencyModel.Reentrant)]

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

[OperationContract(IsOneWay=true)]

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

Для последнего, если ни один из них не помогает, попробуйте отметить вашу реализацию обратного вызова с:

[CallbackBehavior(UseSynchronizationContext=false)]

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

Правка:

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