c# - オブジェクトのリストを単一の応答としてストリーミングし、進捗状況を報告します

原文 c# serialization stream servicestack protobuf-net

私のアプリケーションには「エクスポート」機能があります。機能面では、次のように機能します。

ユーザーが(オプションなどを構成した後)[エクスポート]ボタンを押すと、アプリケーションはまず、エクスポートする必要があるすべてのオブジェクトのIDを決定する比較的迅速なクエリを実行します。次に、オブジェクトごとに、比較的長い時間がかかる計算を実行します(オブジェクトごとに最大1秒)。これが発生している間、ユーザーは進行状況バーを監視しています。これは、予想されるオブジェクトの数と、これまでに処理されたオブジェクトの数がわかっているため、簡単にレンダリングできます。

通常の理由で、この機能をWebサービスに移行したいと思います。ただし、このプロセスのもう1つのしわは、ユーザーが多くのネットワーク待機時間を持っていることが多いことです。したがって、処理する行が1000行ある場合、1000リクエストを実行する余裕はありません。

私がしたいのは、サービスからカスタムストリームを返すことです。行数をストリームの最初の4バイトに書き込むことができます。クライアントは、これらの4バイトを読み取り、進行状況バーを初期化してから、ストリームの読み取りとその場での逆シリアル化に進み、進行状況バーをそれぞれ逆シリアル化するときに更新します。その間、サーバーは、オブジェクトが利用可能になると、ストリームにオブジェクトを書き込みます。

さらに興味深いことに、オブジェクトの長いリストを送り返すので、オーバーヘッドを減らすためにprotobuf-netを使用したいと思います。したがって、私はいくつかの質問があります:


私がやろうとしていることは可能ですか?それは意味がありますか、それとももっと良い方法がありますか?
ServiceStackサービスからカスタムストリームを返すにはどうすればよいですか?
クライアント側でオブジェクトのストリームを逆シリアル化しているときに、各オブジェクトが逆シリアル化されるときに、ある種の通知をどのように取得できますか?プログレスバーを更新するために必要です。


私はこの答えを見つけました。これは私が望んでいることの一種ですが、私の質問には正確に対応していません:Lazy, stream driven object serialization with protobuf-net

編集:私は私のクライアントがServiceStackとprotobuf.netを使用するデスクトップC#アプリケーションであることを述べたはずです。
答え
カスタムレスポンス、カスタムシリアル化、およびカスタムクライアントがストリーミングレスポンスを消費する必要がある結果のストリームを返そうとするのではなく、複数のリクエストにわたって結果セットをページングする(つまり、スキップ/テイクを使用する)ことをお勧めします。これは、ステートレスなアプローチであり、HTTP経由で各クエリを個別にキャッシュできるため、再試行のサポートが向上します。つまり、リクエストの1つにエラーがあった場合、最後に成功した応答から再試行できます(つまり、ダウンロードする必要はありません)リクエスト全体)が改善され、既存のHTTPツールによるデバッグ性とイントロスペクションが向上しました。

カスタムストリーミング応答

以下は、Observable StreamWriterとカスタムのObservableクライアントを返し、ストリーミングされた応答を消費する方法を示す例です。https://gist.github.com/bamboo/5078236
カスタムJSONシリアル化を使用して、各要素がストリームにフラッシュされる前に確実に書き込まれるようにして、ストリームを消費するクライアントが各読み取りでレコード全体を取得できるようにします。プロトコルバッファーのようなバイナリシリアライザーを使用する場合、このカスタムシリアル化はより困難になります。

ServiceStackでバイナリとストリームの応答を返す

ImageServiceは、ServiceStackでバイナリまたはストリーム応答を返すさまざまな方法を示しています。

HttpResultでストリームを返す

public object Any(ImageAsStream request)
{
    using (var image = new Bitmap(100, 100))
    {
        using (var g = Graphics.FromImage(image))
        {
            g.Clear(request.Format.ToImageColor());
        }
        var ms = new MemoryStream();
        image.Save(ms, request.Format.ToImageFormat());
        return new HttpResult(ms, request.Format.ToImageMimeType()); 
    }
}


生のバイトを返す[]

public object Any(ImageAsBytes request)
{
    using (var image = new Bitmap(100, 100))
    {
        using (var g = Graphics.FromImage(image))
        {
            g.Clear(request.Format.ToImageColor());
        }
        using (var m = new MemoryStream())
        {
            image.Save(m, request.Format.ToImageFormat());
            var imageData = m.ToArray(); //buffers
            return new HttpResult(imageData, request.Format.ToImageMimeType());
        }
    }
}


上記の例は、Streamおよびbyte[]応答をHttpResultでラップすることにより、HTTP応答に追加のメタデータを追加する方法を示していますが、必要に応じて、byte[]Streamまたは直接応答します。

応答ストリームに直接書き込む

public void Any(ImageWriteToResponse request)
{
    using (var image = new Bitmap(100, 100))
    {
        using (var g = Graphics.FromImage(image))
        {
            g.Clear(request.Format.ToImageColor());
        }

        base.Response.ContentType = request.Format.ToImageMimeType();
        image.Save(base.Response.OutputStream, request.Format.ToImageFormat());
        base.Response.Close();
    }
}


カスタム結果を返す

public object Any(ImageAsCustomResult request)
{
    var image = new Bitmap(100, 100);
    using (var g = Graphics.FromImage(image))
    {
        g.Clear(request.Format.ToImageColor());
        return new ImageResult(image, request.Format.ToImageFormat()); 
    }
}


IStreamWriterを実装して応答ストリームに直接書き込むことができる場所:

//Your own Custom Result, writes directly to response stream
public class ImageResult : IDisposable, IStreamWriter, IHasOptions
{
    private readonly Image image;
    private readonly ImageFormat imgFormat;

    public ImageResult(Image image, ImageFormat imgFormat = null)
    {
        this.image = image;
        this.imgFormat = imgFormat ?? ImageFormat.Png;
        this.Options = new Dictionary<string, string> {
            { HttpHeaders.ContentType, this.imgFormat.ToImageMimeType() }
        };
    }

    public void WriteTo(Stream responseStream)
    {
        using (var ms = new MemoryStream())
        {
            image.Save(ms, imgFormat);
            ms.WriteTo(responseStream);
        } 
    }

    public void Dispose()
    {
        this.image.Dispose();
    }

    public IDictionary<string, string> Options { get; set; }
}
関連記事

c# - クライアントとWeb API間のプロキシとしてMVCコントローラーを使用する

c# - C#キーを押すとオブジェクトの動きが壊れる

c# - ファイルが存在してもWebブラウザーのナビゲーションがキャンセルされました

c# - ユーザーのADからのオブジェクトGUID

c# - サービス時間を垣間見る方法

c# - log4net翌日の開始時にログファイルを上書きする

c# - ASP.NET MVC(部分的なビュー)を使用して同じページにレビュー製品(http投稿)を追加しようとしています

c# - Model3DCollectionとModel3DGroupの違いは何ですか?

java - 相互互換性のためにC#暗号化をJava(Android)暗号化に変換するヘルプ

c# - Mathos Parser-文字を演算子として使用する