sql >> Database >  >> RDS >> Oracle

Omgaan met grote JSON-gegevens die worden geretourneerd door Web API

Uw probleem is dat u een Oracle-query uitvoert die een zeer groot aantal resultaten retourneert, en vervolgens die volledige resultatenset in het geheugen laadt voordat u deze naar de HttpResponseMessage stuurt. .

Om uw geheugengebruik te verminderen, moet u alle gevallen vinden en elimineren waarin de volledige reeks resultaten van de zoekopdracht wordt geladen in een tijdelijke tussenweergave (bijv. een DataTable of JSON-tekenreeks), en in plaats daarvan de gegevens streamen met behulp van een DataReader . Dit voorkomt dat alles in één keer in het geheugen wordt opgehaald volgens dit antwoord .

Ten eerste blijkt uit uw traceback dat u heeft Browserlink inschakelen gecontroleerd. Aangezien dit blijkbaar probeert het hele antwoord in een MemoryStream te cachen , wilt u het uitschakelen zoals uitgelegd in FilePathResult een OutOfMemoryException met een groot bestand gegooid .

Vervolgens kunt u de inhoud van een IDataReader rechtstreeks naar JSON met behulp van Json.NET met de volgende klasse en converter:

[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();
    }
}

Pas vervolgens uw code aan zodat deze er ongeveer zo uitziet:

    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;
    }

Dit vermijdt de in-memory DataSet weergave van de resultaten.

Overigens denk ik dat de regel

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

in plaats daarvan zou moeten zijn:

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

Ik denk dat je de laatste komma in de query probeert te verwijderen, die aanwezig zal zijn als en alleen als inconditions.Length > 0

Let op:ik ben geen Oracle-ontwikkelaar en ik heb Oracle niet geïnstalleerd. Voor het testen heb ik de OracleClient . gemaakt klassen met een onderliggende OleDbConnection en het werkte prima.



  1. ORA-24247 bij verzending via FTP

  2. Krijgt u het aantal rijen ingevoegd voor ON DUPLICATE KEY UPDATE meerdere inserts?

  3. Gegevens overbrengen van de ene database naar een andere database

  4. Leer hoe je formulieren in het geheugen kunt spawnen (ja, dat heb je goed gehoord)