#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