Отключить автоматическое удаление входных параметров при вызове службы WCF

#c# #wcf

#c# #wcf

Вопрос:

У меня есть сервер WCF, настроенный для создания экземпляра singleton. Мой клиент передает в качестве параметра методу службы объект, который реализует IDisposable интерфейс, и сервер пытается кэшировать этот экземпляр, однако среда выполнения WCF автоматически удаляет параметр в какой-то момент после выполнения метода службы, преждевременно уничтожая мой кэшированный экземпляр.

Просматривая трассировку стека, я нахожу, что Dispose() вызов параметра вызывается внутри MessageRpc.DisposeParametersCore() метода.

Это ссылочный источник для этого метода, взятый из здесь:

  internal void DisposeParametersCore(bool excludeInput)
        {
            if (!this.ParametersDisposed)
            {
                if (!excludeInput)
                {
                    this.DisposeParameterList(this.InputParameters);
                }
                this.DisposeParameterList(this.OutputParameters);
                IDisposable disposableParameter = this.ReturnParameter as IDisposable;
                if (disposableParameter != null)
                {
                    try
                    {
                        disposableParameter.Dispose();
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        this.channelHandler.HandleError(e);
                    }
                }
                this.ParametersDisposed = true;
            }
        }
  

Как вы можете видеть, удаление входных параметров контролируется параметром bool excludeInput , который намекает мне, что такое поведение необязательно.

Я знаю, что если я кэширую глубокую копию параметра, это позволит обойти эту проблему, но есть ли способ отключить это автоматическое поведение для конкретного метода WCF?

Вот класс объекта, который я пытаюсь кэшировать на сервере (написан на C CLI):

 [Serializable()]
   public ref class OpaqueMediaType : ISerializable, IConcreteMediaType {
   private:
      static const Byte _version = 1;
   private:
      clr_scoped_ptr<CComPtrIMFMediaType> _ppMediaType;
   protected:
      virtual DMO_MEDIA_TYPE* __clrcall GetConcreteDMOMediaType() sealed 
         = IConcreteMediaType::GetConcreteDMOMediaType;
      virtual CComPtrIMFMediaType __clrcall  GetConcreteMFMediaType() sealed 
         = IConcreteMediaType::GetConcreteMFMediaType;
      virtual void __clrcall FreeConcreteDMOMediaType(DMO_MEDIA_TYPE* pDMOMediaType) sealed
         = IConcreteMediaType::FreeConcreteDMOMediaType;
   protected:
      OpaqueMediaType(SerializationInfo^ info, StreamingContext context);
   public:
      OpaqueMediaType(DMO_MEDIA_TYPEamp; dmoMediaType);
      OpaqueMediaType(IMFMediaType* pMFMediaType);
      OpaqueMediaType(PCM_MediaType pcmMediaType);
      virtual void __clrcall GetObjectData(SerializationInfo^ info, StreamingContext context);
      PCM_MediaType AsPCM();
   };
  

_ppMediaType Член становится владельцем указателя на производный класс CComPtr, поэтому, когда экземпляр удаляется, связанный COM-объект освобождается. Поскольку этот элемент является одноразовым, IDisposable интерфейс для OpaqueMediaType класса автоматически определяется и реализуется C / CLI.

Вот метод WCF, который кэширует объект:

  Task IStorageBackendSvc.AcceptWmaMediaType(int stationId, OpaqueMediaType mediaType) {
         try {
            WmaWriter wmaWriter = GetWmaWriter(stationId);
            wmaWriter.MediaType = mediaType;  // parameter object is cached here (shallow copy)
            return Task.CompletedTask;
         } catch( Exception exception ) {
            throw _faultFactory.Wrap(exception);
         }
// the `mediaType` parameter is being disposed by WCF at some point AFTER calling this code, releasing the internal COM object held by the cached instance prematurely
      }
  

Комментарии:

1. Я думаю, что нашел это: OperationBehaviorAttribute.AutoDisposeParameters

2. «… сервер пытается кэшировать этот экземпляр …» — как он это делает? Вам нужно показать этот код. Это не должно быть сложно, учитывая, что ваш сервер является синглтоном, просто вставьте его в поле в классе service. Никаких изысков DisposeParametersCore не требуется

3. Вы также понимаете, что любой объект, появляющийся в AppDomain сервера WCF, не имеет ничего общего с представлением объекта клиентом. Даже если на same компьютере находятся и клиент, и сервер WCF, у вас будут отдельные объекты. Изменение клиента не повлияет на сервер и наоборот. WCF нет . СЕТЕВОЕ удаленное управление

4. @MickyD DisposeParametersCore — это внутренний метод WCF, который вызывается автоматически WCF после того, как служба принимает мой запрос. Я также понимаю, что сервер WCF десериализует копию параметра, переданного клиентом, это та копия, которая автоматически удаляется инфраструктурой WCF. Я могу обойти эту проблему, создав и кэшировав глубокую копию параметра, но я хотел знать, могу ли я контролировать это поведение в контракте. По-видимому, это так.

Ответ №1:

Оформление реализации (не контракта) метода с помощью OperationBehaviorAttribute.AutoDisposeParameters = false выполняет свою работу.

Вот мой контракт

 [ServiceContract]
   public interface IStorageBackendSvc {
      ...
      [OperationContract]
      [FaultContract(typeof(DescriptiveFault))]
      Task AcceptWmaMediaType(int stationId, OpaqueMediaType mediaType);

   }
  

Вот реализация:

   [OperationBehavior(AutoDisposeParameters = false)]
  Task IStorageBackendSvc.AcceptWmaMediaType(int stationId, OpaqueMediaType mediaType) {
     try {
        WmaWriter wmaWriter = GetWmaWriter(stationId);
        wmaWriter.MediaType = mediaType;
        return Task.CompletedTask;
     } catch( Exception exception ) {
        throw _faultFactory.Wrap(exception);
     }
  }
  

Теперь mediaType параметр не удален, поэтому кэшированная ссылка остается действительной, и я мог бы обойти выполнение глубокой копии параметра.

Редактировать: Что касается сомнений в том, как кэшируется объект, вот код:

   private Dictionary<int, WmaWriter> _wmaWriters;

  private WmaWriter GetWmaWriter(int stationId) {
         WmaWriter wmaWriter;
         lock( _wmaWriters ) {
            if( !_wmaWriters.TryGetValue(stationId, out wmaWriter) ) {
               wmaWriter = new WmaWriter(stationId, new DailyFileSplitter(), new WmaFileNameResolver(stationId));
               _wmaWriters[stationId] = wmaWriter;
            }
         }
         return wmaWriter;
      }
  

_wmaWriters является членом службы. Это словарь, который содержит несколько программ записи WMA с указанием идентификатора станции (я записываю несколько телевизионных и радиостанций) GetWmaWriter() Метод вернет существующий WMA writer, связанный с определенной станцией, или создаст новый, если таковой не существует. Кэширование эффективно, потому что при нескольких вызовах для одного и того же идентификатора станции я получу один и тот же экземпляр WmaWriter.

Комментарии:

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

2. @MickyD Я добавил логику, которая кэширует mediaType параметр. Как вы можете видеть, он является корневым, поскольку я присваиваю его свойству MediaType объекта, время жизни которого контролируется самой службой. Дело в том, что даже если объект был внедрен, WCF настаивал на его автоматическом удалении, независимо от того, что действительная ссылка на него все еще сохранялась.

3. Нет, в этом случае потоковой передачи нет. В основном клиент WCF определяет качество записи WMA (скорость передачи данных, размер выборки, частота дискретизации и т.д.). Клиенту необходимо передать эту конфигурацию удаленному компоненту записи WMA, который будет получать сжатые аудиопакеты от клиента. Эта информация хранится в OpaqueMediaType экземпляре, который является сериализуемым и, следовательно, может передаваться через WCF…

4. Ничего себе, хорошо. Рад, что у вас все получилось. 1

5. @MickyD, спасибо за ваш вклад. Извините, что я пропустил много информации, которая была необходима для правильного контекста. Приветствия! =D