Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Radzenie sobie z dużymi danymi JSON zwracanymi przez Web API

Twój problem polega na tym, że uruchamiasz zapytanie Oracle, które zwraca bardzo dużą liczbę wyników, a następnie ładuje cały zestaw wyników do pamięci przed serializacją do HttpResponseMessage .

Aby zmniejszyć zużycie pamięci, należy znaleźć i wyeliminować wszystkie przypadki, w których cały zestaw wyników zapytania jest ładowany do tymczasowej reprezentacji pośredniej (np. DataTable lub ciąg JSON), a zamiast tego przesyłaj dane strumieniowo przy użyciu DataReader . Pozwala to uniknąć ściągania wszystkiego do pamięci na raz zgodnie z ta odpowiedź .

Po pierwsze, z Twojego śledzenia wynika, że ​​masz Włącz link do przeglądarki w kratę. Ponieważ najwyraźniej próbuje to buforować całą odpowiedź w MemoryStream , będziesz chciał go wyłączyć, jak wyjaśniono w FilePathResult rzucił wyjątek OutOfMemoryException z dużym plikiem .

Następnie możesz przesyłać strumieniowo zawartość IDataReader bezpośrednio do JSON za pomocą Json.NET z następującą klasą i konwerterem:

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

Następnie zmodyfikuj swój kod, aby wyglądał mniej więcej tak:

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

Pozwala to uniknąć znajdującego się w pamięci DataSet reprezentacja wyników.

Nawiasem mówiąc, liczę na linię

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

zamiast tego powinno być:

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

Uważam, że próbujesz usunąć końcowy przecinek w zapytaniu, który będzie obecny wtedy i tylko wtedy, gdy inconditions.Length > 0

Uwaga - nie jestem programistą Oracle i nie mam zainstalowanego Oracle. Do testów wyśmiewałem OracleClient klasy używające bazowego OleDbConnection i działało dobrze.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Znajdź wartości, które nie istnieją w tabeli

  2. PLS-00103:Napotkano symbol „koniec pliku” w prostym bloku aktualizacji

  3. Dlaczego nie mogę zaktualizować więcej niż jednej kolumny jednocześnie za pomocą słowa kluczowego?

  4. ORA-16205 Aktualizacja do 11.2.0.3

  5. Oracle LIMIT n,m ekwiwalent