Отключить автоматическое удаление входных параметров в вызове службы WCF - PullRequest
3 голосов
/ 28 марта 2019

У меня есть сервер WCF, настроенный для одноэлементного копирования.Мой клиент передает в качестве параметра методу службы, объект, который реализует интерфейс 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;
            }
        }

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

Я знаю, что если я кеширую глубокую копию параметра, это обойдет эту проблему, но есть ли способ отключить это автоматическое поведение для определенного метода 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_TYPE& 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 Ответ

1 голос
/ 28 марта 2019

Украшение реализации (не контракта) метода с помощью 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, связанный с конкретной станцией, или создает новый, если его нет.существует.Кэширование эффективно, потому что для нескольких вызовов с одним и тем же идентификатором станции я получу один и тот же экземпляр WmaWriter.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...