Da C#, utilizzando odp.net, chiamo una funzione oracle che restituisce un cursore. Alcune delle colonne sono di tipo "timestamp con fuso orario" (TSTZ). Se utilizzo direttamente un OracleDataAdapter, tali colonne vengono convertite in System.DateTime e le informazioni sul fuso orario vengono perse. Questo è un comportamento previsto e la raccomandazione sembra essere quello di utilizzare SafeMapping per forzare la conversione in stringa del tipo:oracle odp.net Conversione SafeMapping se timestamp con fuso orario - come ottenere offset al posto del nome della zona
dataAdapter.SafeMapping.Add("column_name", typeof(string));
Ho poi giunga la TSTZ come una stringa, ma è con il formato DD-MON-YYYY HH:MI:SS.FF AM TZR
in questo modo:
23-NOV-12 08.10.12.057868000 PM ASIA/CALCUTTA
Quello che voglio invece l'offset (ad esempio formato DD-MON-YYYY HH:MI:SS.FF AM TZH:TZD
come:
23-NOV-12 08.10.12.057868000 PM +04:30
Quando ho query di Oracle direttamente (diciamo in S ql Developer), posso usare
Alter Session Set Nls_Timestamp_Tz_Format='DD-MON-YYYY HH:MI:SS.FF AM TZH:TZM'
per ottenere il formato desiderato. Uso odp.net ho provato sia modificando il formato in SetSessionInfo:
connection.Open();
OracleGlobalization glob = connection.GetSessionInfo();
glob.TimeStampTZFormat = "DD-MON-YYYY HH:MI:SS.FF AM TZH:TZM";
connection.SetSessionInfo(glob);
nonché eseguire il comando alter session
utilizzando la stessa connessione, ma non ha alcun effetto. Presumo questo perché la conversione in stringa avviene in una fase successiva e le impostazioni di connessione non hanno alcun effetto.
C'è qualche altro modo per avere Odp.net fornirmi l'offset direttamente? Non riesco a modificare la funzione db di Oracle, quindi l'utilizzo di tz_offset nel metodo non è un'opzione.
Se ciò non è possibile, qual è il modo migliore per convertire la stringa del fuso orario in offset? Attualmente sto pensando di esecuzione di un
select TZNAME, TZABBREV, tz_offset(TZNAME) as TZOFFSET
from V$TIMEZONE_NAMES
una volta per costruire un LOOKUPTABLE, ma sarei felice se ci sono opzioni migliori.
Il mio codice di recupero dei dati, comprese le cose che ho provato:
using (var connection = new OracleConnection(this.connectionString))
{
connection.Open();
OracleGlobalization glob = connection.GetSessionInfo();
glob.TimeStampTZFormat = "DD-MON-YYYY HH:MI:SS.FF AM TZH:TZM";
connection.SetSessionInfo(glob);
string sql = "ALTER SESSION " +
"SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"'";
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "fn_name";
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = false;
var output = cmd.Parameters.Add("return_value", OracleDbType.RefCursor);
output.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add("id", id).Direction = ParameterDirection.Input;
using (var dataAdapter = new OracleDataAdapter(cmd))
{
dataAdapter.SafeMapping.Add("TZ_COLUMN", typeof(string));
dataAdapter.TableMappings.Add("Table", "Table");
OracleGlobalization glob2 = connection.GetSessionInfo();
glob2.TimeStampTZFormat = "DD-MON-YYYY HH:MI:SS.FF AM TZH:TZM";
connection.SetSessionInfo(glob2);
dataAdapter.Fill(dataSet);
}
foreach (DataRow row in dataSet.Tables[0].Rows)
{
// column is string with timezone name, I want offset
}
}
Grazie
Scartare implicitamente le informazioni sul fuso orario suona più come un errore piuttosto che come un comportamento previsto per me. Dato che non ho familiarità con OPD.NET il seguente è un'ipotesi, ma potresti probabilmente avvolgere la chiamata di funzione in un'altra stored procedure che inoltra la chiamata alla funzione originale. Il wrapper (che viene eseguito sul database prima della conversione del tipo) potrebbe estrarre il fuso orario e riportarlo come colonna aggiuntiva. –
Grazie, ma purtroppo non posso cambiare il metodo. Tuttavia, ho appena capito cosa sembra un modo molto semplice per rendere il DataAdapter restituire tipi specifici di Oracle. –