Предотвращение проблемы взаимоблокировки с помощью службы двустороннего обратного вызова WCF

У меня возникла проблема с самообслуживанием wcf duplex callback. Я получаю сообщение InvalidOperationException с сообщением:

Эта операция зашла бы в тупик, потому что ответ не может быть получен пока текущее сообщение не завершит обработку. Если вы хотите разрешить обработка сообщений вне очереди, укажите ConcurrencyMode реентера или Multiple on CallbackBehaviorAttribute.

Вот мое поведение службы:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode =  ConcurrencyMode.Reentrant, UseSynchronizationContext = true)]

Вот мой контракт на обслуживание:

 [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]

[ServiceContract]
public interface IClientToService
{
    [OperationContract(IsOneWay = false)]
    LVSSStatus GetLvssStatus();

    [OperationContract(IsOneWay = true)]
    void PickSpecimen(long trackingNumber, int destCode);

    [OperationContract(IsOneWay = true)]
    void CancelCurrentPickTransaction();
}

Вот мой интерфейс обратного вызова:

public interface ILvssClientCallback
{
    [OperationContract(IsOneWay = true)]
    void SendClientCallback(LvssCallbackMessage callbackMessage);

    [OperationContract(IsOneWay = false)]
    List<SpecimenTemplateDescriptor> GetTemplateDescriptorList(DrawerLayout drawerLayout);

    [OperationContract(IsOneWay = false)]
    SpecimenTemplate SelectSpecimenTemplate(string templateName, int version);

    [OperationContract]
    void SpecimenStoredInContainer(string containerID, bool isValidRackID, int rackRow, int rackCol, int deckRow, int deckCol,
     int drawerRow, int drawerCol, long trackingNumber, RobotErrors robotError);

    [OperationContract]
    void LvssRobotStatusChange(LVSSStatus status);
}

Я понимаю, что InvalidOperationException вызывается, когда на клиенте вызывается операция обратного вызова, служба уже заблокирована для обработки текущей операции. Таким образом, происходит взаимоблокировка.

Я попытался изменить мой ConcurrencyMode на несколько, а UseSynchronizationContext - на false.

Я все еще вижу две проблемы с моей службой:

В первую очередь: Следующая служебная операция отменяет мое клиентское приложение wpf, когда GetLvssStatus() вызывается быстро (быстро нажав кнопку пользовательского интерфейса). Этот метод не является одним из способов, и синхронно возвращает перечисляемый тип из службы обратно клиенту.

    [OperationContract(IsOneWay = false)]
    LVSSStatus GetLvssStatus();

* Что заставляет мое приложение wpf замерзать в этом случае? * Что я могу сделать, чтобы предотвратить зависание приложения? Если я использую фоновый поток в качестве асинхронного вызова, приложение не замерзает. Мне действительно нужен этот метод для работы синхронно.

Второе: Когда я назначаю метод обратного вызова LvssRobotStatusChange на IsOneWay = true, я получаю ObjectDisposedException: не могу получить доступ к удаленному объекту. Имя объекта: 'System.ServiceModel.Channels.ServiceChannel'.

    [OperationContract(IsOneWay = true)]
    void LvssRobotStatusChange(LVSSStatus status);

* Что вызывает это исключение ObjectDisposedException? * Можно ли опустить назначение IsOneWay в этом случае? Опущение IsOneWay в этом случае позволяет завершить обратный вызов без каких-либо исключений.

* Могут ли эти проблемы возникнуть из-за отсутствия безопасного потока? * Если да, то какова наилучшая практика для обеспечения безопасности потока ConcurrencyMode.Multiple?

Любая помощь с этими вопросами очень ценится.

* ПЕРВЫЙ РЕДАКТИРОВАНИЕ Немного больше информации о создании моего дуплексного канала. Моя модель просмотра wpf создает прокси-объект, который отвечает за обработку моего канала. Любые попытки до сих пор установить канал в новом потоке на стороне клиента приводят к исключению ObjectDisposedException, когда служба пытается использовать объект обратного вызова.

* ВТОРАЯ РЕДАКТИРОВКА Я считаю, что моя служба должна работать, если я могу получить контракты на работу с методом void, чтобы установить IsOneWay = true. При использовании concurrency реентерабера основной поток канала должен пропускать эти методы независимо от какой-либо блокировки.
Вот мой интерфейс обратного вызова:

public interface ILvssClientCallback
{
    [OperationContract(IsOneWay = true)]
    void SendClientCallback(LvssCallbackMessage callbackMessage);

    [OperationContract]
    List<SpecimenTemplateDescriptor> GetTemplateDescriptorList(DrawerLayout drawerLayout);

    [OperationContract]
    SpecimenTemplate SelectSpecimenTemplate(string templateName, int version);

    [OperationContract(IsOneWay = true)]
    void SpecimenStoredInContainer(string containerID, bool isValidRackID, int rackRow, int rackCol, int deckRow, int deckCol,
     int drawerRow, int drawerCol, long trackingNumber, RobotErrors robotError);

    [OperationContract(IsOneWay = true)]
    void LvssRobotStatusChange(LVSSStatus status);
}

Когда я устанавливаю метод контракта LvssRobotStatuschange на IsOneWay = true, мой канал кэширования обратного вызова генерирует исключение CommunicationObjectAbortedException. По какой-то причине мое свойство обратного вызова прерывается.

*** Что может привести к отказу канала обратного вызова?

Ответ 1

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

Ответ 2

Проблема, с которой я столкнулся:

CallBackHandlingMethod()
{
    requestToService();    // deadlock message.    
}

выход:

CallBackHandlingMethod()
{
    Task.Factory.StartNew(()=>
    {
        requestToService();
    });
}

Ответ 3

У меня была аналогичная проблема, которую я решил просто добавив

[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]

для моей реализации обратного вызова.

Ответ 4

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServiceCallbackHandler : IServiceCallback
{
 ...
}