sql >> データベース >  >> RDS >> Oracle

Web API によって返される大きな JSON データの処理

    問題は、非常に多数の結果を返す Oracle クエリを実行し、その結果セット全体をメモリにロードしてから、HttpResponseMessage にシリアル化することです。 。

    メモリ使用量を減らすには、クエリの結果セット全体が一時的な中間表現 (例:DataTable または JSON 文字列)、代わりに データリーダー 。これにより、この回答

    まず、トレースバックから、 があるようですブラウザ リンクを有効にする チェック済み。これは明らかに応答全体を MemoryStream にキャッシュしようとするためです FilePathResult で説明されているように、無効にする必要があります。大きなファイルで OutOfMemoryException をスローしました

    次に、IDataReader のコンテンツをストリーミングできます。 次のクラスとコンバーターで Json.NET を使用して JSON に直接:

    [JsonConverter(typeof(OracleDataTableJsonResponseConverter))]
    public sealed class OracleDataTableJsonResponse
    {
        public string ConnectionString { get; private set; }
        public string QueryString { get; private set; }
        public OracleParameter[] Parameters { get; private set; }
    
        public OracleDataTableJsonResponse(string connStr, string strQuery, OracleParameter[] prms)
        {
            this.ConnectionString = connStr;
            this.QueryString = strQuery;
            this.Parameters = prms;
        }
    }
    
    class OracleDataTableJsonResponseConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(OracleDataTableJsonResponse);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException("OracleDataTableJsonResponse is only for writing JSON.  To read, deserialize into a DataTable");
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var response = (OracleDataTableJsonResponse)value;
    
            using (var dbconn = new OracleConnection(response.ConnectionString))
            {
                dbconn.Open();
                using (var selectCommand = new OracleCommand(response.QueryString, dbconn))
                {
                    if (response.Parameters != null)
                        selectCommand.Parameters.AddRange(response.Parameters);
                    using (var reader = selectCommand.ExecuteReader())
                    {
                        writer.WriteDataTable(reader, serializer);
                    }
                }
            }
        }
    }
    
    public static class JsonExtensions
    {
        public static void WriteDataTable(this JsonWriter writer, IDataReader reader, JsonSerializer serializer)
        {
            if (writer == null || reader == null || serializer == null)
                throw new ArgumentNullException();
            writer.WriteStartArray();
            while (reader.Read())
            {
                writer.WriteStartObject();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    writer.WritePropertyName(reader.GetName(i));
                    serializer.Serialize(writer, reader[i]);
                }
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
        }
    }
    

    次に、コードを次のように変更します。

        public HttpResponseMessage Getdetails([FromUri] string[] id)
        {
            var prms = new List<OracleParameter>();
            var connStr = ConfigurationManager.ConnectionStrings["PDataConnection"].ConnectionString;
            var inconditions = id.Distinct().ToArray();
            var strQuery = @"SELECT 
                           STCD_PRIO_CATEGORY_DESCR.DESCR AS CATEGORY, 
                           STCD_PRIO_CATEGORY_DESCR.SESSION_NUM AS SESSION_NUMBER, 
                           Trunc(STCD_PRIO_CATEGORY_DESCR.START_DATE) AS SESSION_START_DATE, 
                           STCD_PRIO_CATEGORY_DESCR.START_DATE AS SESSION_START_TIME , 
                           Trunc(STCD_PRIO_CATEGORY_DESCR.END_DATE) AS SESSION_END_DATE, 
                             FROM 
                             STCD_PRIO_CATEGORY_DESCR, 
                             WHERE 
                            STCD_PRIO_CATEGORY_DESCR.STD_REF IN(";
            var sb = new StringBuilder(strQuery);
            for (int x = 0; x < inconditions.Length; x++)
            {
                sb.Append(":p" + x + ",");
                var p = new OracleParameter(":p" + x, OracleDbType.NVarchar2);
                p.Value = inconditions[x];
                prms.Add(p);
            }
            if (sb.Length > 0)// Should this be inconditions.Length > 0  ?
                sb.Length--;
            strQuery = sb.Append(")").ToString();
    
            var returnObject = new { data = new OracleDataTableJsonResponse(connStr, strQuery, prms.ToArray()) };
            var response = Request.CreateResponse(HttpStatusCode.OK, returnObject, MediaTypeHeaderValue.Parse("application/json"));
            ContentDispositionHeaderValue contentDisposition = null;
            if (ContentDispositionHeaderValue.TryParse("inline; filename=ProvantisStudyData.json", out contentDisposition))
            {
                response.Content.Headers.ContentDisposition = contentDisposition;
            }
            return response;
        }
    

    これにより、メモリ内の DataSet が回避されます 結果の表現。

    ちなみに、私はラインを計算します

            if (sb.Length > 0)
                sb.Length--;
    

    代わりに:

            if (inconditions.Length > 0)
                sb.Length--;
    

    inconditions.Length> 0 の場合にのみ存在する、クエリの末尾のコンマを剥がそうとしていると思います

    注 - 私は Oracle 開発者ではなく、Oracle をインストールしていません。テストのために OracleClient をモックアップしました 基礎となる OleDbConnection を使用するクラス そしてうまくいきました。



    1. postgresqlgemasyncを使用する

    2. 結果によってグループの複数の値を取得するHAVING句のロジック

    3. Oracle 11gの選択クエリにオフセットを追加するにはどうすればよいですか?

    4. mysqlleftjoinはすべての左テーブル行を返さない