Использование ключевого слова yield в цикле для обновления индикатора выполнения в C # - PullRequest
1 голос
/ 14 мая 2011

Могу ли я обновить ход цикла, используя ключевое слово yield?

foreach(something obj in somelist)
{
    yield return obj;
}

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

Спасибо.

Ответы [ 3 ]

1 голос
/ 14 мая 2011

На основании вашего комментария, что вы

пытается обновить прогресс передача данных в сервисе wcf форма окна

Вот решение, которое я использовал в недавнем проекте. Пример кода предназначен для загрузки файла (т. Е. Pdf, jpg и т. Д.), Но может применяться к любым потоковым данным.

Первое важное замечание, ваш выбор привязки WCF определит, возможно ли это вообще. Если вы используете basicHttpBinding или netTcpBinding, то подход должен работать, однако если вы используете WsHttpBinding, вам не повезло.

Пример привязки может выглядеть примерно так:

  <basicHttpBinding>
    <binding name="ResourceServiceBasicHttpBinding" maxReceivedMessageSize="20971520" sendTimeout="00:05:00" receiveTimeout="00:05:00" transferMode="Streamed" messageEncoding="Mtom">
      <security mode="None" />
    </binding>
  </basicHttpBinding>

Важно отметить, что режим передачи "Потоковый".

Чтобы сообщить о ходе передачи данных между службой WCF и WinForm, необходимо убедиться, что ваша служба WCF использует MessageContracts поверх DataContracts. Причина в том, что MessageContract позволяет вам иметь один поток в качестве тела ответа. Например, у вас может быть договор на запрос / ответ на сообщение:

Запрос ...

[MessageContract]
public class DownloadFileRequest
{
  [MessageHeader(MustUnderstand = true)]
  public Guid FileId { get; set; }
}

Ответ ...

[MessageContract]
public class DownloadFileResponse : IDisposable
{
  [MessageHeader(MustUnderstand = true)]
  public Int32 FileSize { get; set; }

  [MessageHeader(MustUnderstand = true)]
  public String FileName { get; set; }

  [MessageBodyMember(Order = 1)]
  public Stream FileByteStream { get; set; }

  public void Dispose()
  {
    if (FileByteStream != null)
      FileByteStream.Dispose();
  }
}

Теперь все хорошо, но у вас должен быть способ сообщить клиенту о ходе загрузки (или выгрузки). К сожалению, .NET не предоставляет хороший способ сделать это ... как уже упоминалось, вы ищете события ... в частности, реализацию "ObservableStream". Опять же, вот реализация, которую я люблю использовать ....

Событие устанавливает, чтобы сообщить оператору чтения или записи подписчику ...

public class ObservableStreamEventArgs : EventArgs
{
  private readonly Int64 _length;
  private readonly Int64 _position;
  private readonly Int32 _numberOfBytes;

  public Int64 StreamLength { get { return _length; } }
  public Int64 StreamPosition { get { return _position; } }
  public Int32 NumberOfBytes { get { return _numberOfBytes; } }

  public ObservableStreamEventArgs(Int32 numberOfBytes, Int64 position, Int64 length)
  {
    _numberOfBytes = numberOfBytes;
    _position = position;
    _length = length;
  }
}

И, конечно, сама реализация потока ...

public class ObservableStream : Stream
{
  private readonly Stream _baseStream;
  private Int64 _streamLength;

  public event EventHandler<ObservableStreamEventArgs> BytesRead;
  public event EventHandler<ObservableStreamEventArgs> BytesWritten;

  public ObservableStream(Stream stream)
  {
    Verify.NotNull(stream);

    _baseStream = stream;
    _streamLength = _baseStream.Length;
  }

  public ObservableStream(Stream stream, Int64 length)
  {
    Verify.NotNull(stream);

    _baseStream = stream;
    _streamLength = length;
  }

  public override bool CanRead      
  {        
    get { return _baseStream.CanRead; }
  }

  public override bool CanSeek
  {
    get { return _baseStream.CanSeek; }
  }

  public override bool CanWrite
  {
    get { return _baseStream.CanWrite; }
  }

  public override void Flush()
  {
    _baseStream.Flush();
  }

  public override Int64 Length
  {
      get { return _streamLength; }
    }

    public override Int64 Position
    {
      get { return _baseStream.Position; }
      set { _baseStream.Position = value; }
    }

    public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
    {
      Int32 bytesRead = _baseStream.Read(buffer, offset, count);

      var listeners = BytesRead;
      if (listeners != null)
        listeners.Invoke(this, new ObservableStreamEventArgs(bytesRead, Position, Length));

      return bytesRead;
    }

    public override Int64 Seek(Int64 offset, SeekOrigin origin)
    {
      return _baseStream.Seek(offset, origin);
    }

    public override void SetLength(Int64 value)
    {
      _streamLength = value;
    }

    public override void Write(Byte[] buffer, Int32 offset, Int32 count)
    {
      _baseStream.Write(buffer, offset, count);

      var listeners = BytesWritten;
      if (listeners != null)
        listeners.Invoke(this, new ObservableStreamEventArgs(count, Position, Length));
    }

    public override void Close()
    {
      _baseStream.Close();

      base.Close();
    }

    protected override void Dispose(bool disposing)
    {
      if (disposing)
        _baseStream.Dispose();

      base.Dispose(disposing);
    }
  }

Наконец, вы должны использовать данные в вашем клиенте Windows ...

Просто вызовите ваш сервисный метод, как обычно (ServiceChannelFactory - это оболочка для реализации ChannelFactory ... не беспокойтесь об этом).

 DownloadFileResponse response;
 using (var channel = _serviceChannelFactory.CreateReportServiceChannel())
   response channel.Service.GenerateReport(request)

и затем подключитесь к наблюдаемому потоку, чтобы обновить пользовательский интерфейс (очевидно, он должен работать в фоновом потоке, иначе вы просто заблокируете пользовательский интерфейс)

 using (var downloadStream = response.FileByteStream)
 using (var observableStream = new ObservableStream(downloadStream, response.FileSize))
 using (var fileStream = new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.Write))
 {
   observableStream.BytesRead += (sender, e) => _view.UpdateProgress(Convert.ToInt32(e.StreamPosition * 100 / e.StreamLength));

   do
   {
     bytesRead = observableStream.Read(buffer, 0, 4096);
     fileStream.Write(buffer, 0, bytesRead);
   } while (bytesRead > 0);
 }

EDIT

О, сам метод обслуживания довольно прост ... забыл его включить; все, что вам нужно сделать, это вернуть ваш поток:

  return new DownloadFileResponse
           {
             FileName = report.FileName,
             FileSize = report.Data.Length,
             FileByteStream = new MemoryStream(report.Data, false)
           };
0 голосов
/ 14 мая 2011

Почему бы не обновить индикатор выполнения из цикла?

0 голосов
/ 14 мая 2011

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

Но если вам нужно решение, которое обновляет прогресс на yield return, вы, вероятно, захотите получитьколичество элементов перед повторением вашей коллекции.Затем для каждого возвращаемого элемента вы можете обновить свой индикатор прогресса на единицу.

Возможно, было бы полезно, если бы вы немного проработали свой вопрос - что именно вы пытаетесь достичь.

...