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.