Почему WCF считывает входной поток в EOF в Close()?

Мы используем WCF для создания простой веб-службы, которую наш продукт использует для загрузки больших файлов по WAN-ссылке. Он должен быть простым HTTP PUT, и он работает нормально по большей части.

Здесь упрощенная версия контракта на обслуживание:

[ServiceContract, XmlSerializerFormat]
public interface IReplicationWebService
{
 [OperationContract]
 [WebInvoke(Method = "PUT", UriTemplate = "agents/{sourceName}/epoch/{guid}/{number}/{type}")]
 ReplayResult PutEpochFile(string sourceName, string guid, string number, string type, Stream stream);
}

При реализации этого контракта мы считываем данные из stream и записываем его в файл. Это отлично работает, поэтому мы добавили некоторую обработку ошибок для случаев, когда на диске недостаточно места для хранения файла. Здесь примерно то, на что оно похоже:

public ReplayResult PutEpochFile(string sourceName, string guid, string number, string type, Stream inStream)
 {
 //Stuff snipped
 try
 {
 //Read from the stream and write to the file
 }
 catch (IOException ioe)
 {
 //IOException may mean no disk space
 try
 {
 inStream.Close();
 }
 // if instream caused the IOException, close may throw
 catch
 {
 }
 _logger.Debug(ioe.ToString());
 throw new FaultException<ioexception>(ioe, new FaultReason(ioe.Message), new FaultCode("IO"));
 }
 }
</ioexception>

Чтобы проверить это, я отправляю файл на 100 ГБ на сервер, на котором не хватает места для файла. Как и ожидалось, это вызывает исключение, но вызов inStream.Close(), казалось, зависал. Я проверил его, и на самом деле происходит то, что вызов Close() пробился через сантехнику WCF до тех пор, пока он не достигнет System.ServiceModel.Channels.DrainOnCloseStream.Close(), который согласно Reflector выделяет буфер Byte[] и продолжает чтение из потока до тех пор, пока он не будет EOF.

Другими словами, вызов Close считывает все 100GB тестовых данных из потока перед возвратом!

Теперь может случиться так, что мне не нужно вызывать Close() в этом потоке. Если в этом случае мне хотелось бы объяснить, почему. Но что более важно, я был бы признателен, если бы кто-нибудь мог объяснить мне, почему Close() ведет себя таким образом, почему он не считается ошибкой и как перенастроить WCF, чтобы этого не произошло.

1 ответ

.Close() предназначен для "безопасного" и "дружественного" способа остановки вашей работы - и он действительно выполнит текущие запросы перед закрытием - по дизайну.

Если вы хотите сбросить кувалду, вместо этого используйте .Abort() на прокси-сервере клиента (или хосте службы). Это просто отключает все, не проверяя и не заботясь о том, чтобы ждать завершения операций.

licensed under cc by-sa 3.0 with attribution.