На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
  
> Странности WCF , в консоле работает, в WPF и Winforms - нет
    Нашел вот такую забавную штуку есть простенький сервис WCF, который по таймеру отдает клиентам сообщение. Также клиент может сам полезть за сообщением на сервер.

    Сервер:
    Контракт сервиса
    ExpandedWrap disabled
          [ServiceContract(CallbackContract = typeof(IClientCallback))]
          public interface ISampleService
          {
              [OperationContract(IsOneWay = true)]
              void RegisterClient();
       
              [OperationContract(IsOneWay = true)]
              void UnregisterClient();
       
              [OperationContract]
              String RecieveString();
          }

    Контракт коллбека
    ExpandedWrap disabled
          [ServiceContract]
          public interface IClientCallback
          {
              [OperationContract(IsOneWay = true)]
              void NewMessage(String message);
          }

    Реализация сервиса
    ExpandedWrap disabled
          [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
          public class SampleService : ISampleService
          {
              private List<IClientCallback> _Callbacks = new List<IClientCallback>();
              private object lockCallbacks = new object();
       
              private Timer _TimerCallbacks;
       
              public SampleService()
              {
                  _TimerCallbacks = new Timer(OnTimer, null, 0, 700);
              }
       
              // timer callback
              public void OnTimer(object state)
              {
                  lock (lockCallbacks)
                  {
                      for (int i = 0; i < _Callbacks.Count; i++)
                          try
                          {
                              _Callbacks[i].NewMessage(String.Format("Client callback", DateTime.Now));
                          }
                          catch (CommunicationException)
                          {
                              // channel faulted
                              _Callbacks.RemoveAt(i);
                              i--;
                          }
                  }
              }
       
              #region ISampleService Members
       
              public void RegisterClient()
              {
                  OperationContext context = OperationContext.Current;
                  IClientCallback c = context.GetCallbackChannel<IClientCallback>();
                  lock (lockCallbacks)
                      _Callbacks.Add(c);
              }
       
              public void UnregisterClient()
              {
                  OperationContext context = OperationContext.Current;
                  IClientCallback c = context.GetCallbackChannel<IClientCallback>();
                  lock (lockCallbacks)
                      _Callbacks.Remove(c);
              }
       
              public string RecieveString()
              {
                  return "Client request";
              }
       
              #endregion
          }

    Теперь само создание сервиса
    ExpandedWrap disabled
                  SampleService instance = new SampleService();
                  ServiceHost host = new ServiceHost(instance);
       
                  NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
                  binding.MaxReceivedMessageSize = Int32.MaxValue;
       
                  host.AddServiceEndpoint(typeof(ISampleService),
                      binding,
                      new Uri("net.tcp://localhost:888/SampleService"));
       
                  // set all throttlings to maximum to ensure the problem is not here
                  ServiceThrottlingBehavior bhvrThrottling = new ServiceThrottlingBehavior();
                  bhvrThrottling.MaxConcurrentCalls = Int32.MaxValue;
                  bhvrThrottling.MaxConcurrentInstances = Int32.MaxValue;
                  bhvrThrottling.MaxConcurrentSessions = Int32.MaxValue;
       
                  host.Description.Behaviors.Add(bhvrThrottling);
       
                  // mex
                  host.Description.Behaviors.Add(new ServiceMetadataBehavior());
                  Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
                  host.AddServiceEndpoint(typeof(IMetadataExchange),
                      mexBinding,
                      new Uri("net.tcp://localhost:888/SampleService/mex"));
       
                  host.Open();

    Клиент:
    Для клиента сгенерен прокси класс, а также написана реализация коллбека
    ExpandedWrap disabled
          class ServiceCallback : ISampleServiceCallback
          {
              #region ISampleServiceCallback Members
       
              public void NewMessage(string message)
              {
                  Debug.WriteLine(message);
              }
       
              #endregion
          }


    А теперь начинается самое интересное. Есть следующий код, который подписывается на сообщения от сервера и в свою очередь сам запрашивает сообщение каждые 700мс
    ExpandedWrap disabled
                  ServiceCallback service = new ServiceCallback();
                  SampleServiceClient client = new SampleServiceClient(new InstanceContext(service));
                  
                  client.RegisterClient();
                  Debug.WriteLine("Connected...");
       
                  for (;;)
                  {
                      Debug.WriteLine(client.RecieveString());
                      Thread.Sleep(700);
                  }


    1) Если вставляю это в консольное приложение - работает как часы.
    2) WinForm и WPF в событии Load (Loaded) - рушатся с эксепшеном TimeoutException на client.RecieveString()
    3) WinForm и WPF в Main перед Application.EnableVisualStyles(); - работает как нужно

    Где туплю?
    Полный пример см в аттаче.

    PS Переименовать с WCF1.jpg в WCF1.rar
    PPS используется класс Debug
    Прикреплённый файлПрикреплённый файлWCF1.jpg (162.37 Кбайт, скачиваний: 214)
      Еесли запустить это в отдельном потоке, то все работает. Предполагаю, что это как-то связано с однопоточностью интерфейса. Только как?? Так не должно быть
      ExpandedWrap disabled
                    ThreadPool.QueueUserWorkItem((s) =>
                        {
                            ServiceCallback service = new ServiceCallback();
                            SampleServiceClient client = new SampleServiceClient(new InstanceContext(service));
                            client.RegisterClient();
                            Debug.WriteLine("Connected...");
         
                            for (; ; )
                            {
                                Debug.WriteLine(client.RecieveString());
                                Thread.Sleep(700);
                            }
                        });
        Ответ нашел здесь, codehelper.ru :

        По умолчанию в WCF callbacks настроены так, чтобы вылеть в том же потоке, в котором создан сам callback-объект. Если callback-объект создается в потоке, связанном с GUI (например в форме WinForms), то он становится привязанным к этому потоку. Это создает ситуацию аналогичную deadlock`у: callback ждет когда закончит выполнение инициировавший его метод, а метод не может закончиться, потому что ждет когда выполнится callback. В итоге метод вызова сервиса прекращает работу с сообщением о таймауте. Исправляется это следующим образом:

        Изменяем ConcurrencyMode в поведении сервиса на Reentrant:

        ExpandedWrap disabled
          [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
                           ConcurrencyMode=ConcurrencyMode.Reentrant)]
          public class SampleService : ISampleService
          {
              // ...
          }


        Устанавливаем значение свойства UseSynchronizationContext в false для поведения callback-объекта:
        ExpandedWrap disabled
          [CallbackBehavior(UseSynchronizationContext = false)]
          class ServiceCallback : ISampleServiceCallback
          {
              // ...
          }
          GarF1eld Спасибо, огромное.
          Случилось аналогичная ситуация, не знал что делать. Отлично помог.
          Сообщение отредактировано: RDAlex -
          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
          0 пользователей:


          Рейтинг@Mail.ru
          [ Script execution time: 0,0392 ]   [ 16 queries used ]   [ Generated: 17.05.24, 16:41 GMT ]