NHibernate: presnost ulozenia DateTime typu
Ak potrebujete ulozit typ DateTime s presnostou na milisekundy (napr ukladam transakcie a tam potrebujem co najvyssiu presnost), moze byt neprijemne zistit, ze NHibernate v kombinacii s MSSQL serverom (a tipujem, ze sa to tyka aj dalsich db serverov) vam milisekundovu cast jednoducho odsekne.
Preco sa to deje? Nativny .NET DateTime typ uklada cas - konkretne milisekundy s presnostou na 7 miest. MSSQL nativny DateTime iba 3 (DateTime2 uz na 7, vid t-sql msdn doc: http://msdn.microsoft.com/en-us/library/ms187819.aspx). NH standardne pouziva ADO.NET pre komunikaciu s MSSQL, a ten nam nic presnejsie ako mu databaza ponuka nevrati.
Kvoli tymto rozdielom NH odsekava milisekundovu cast a nechava tuto funkcnost na uzivatela.
Mozne riesenia:
1) ako prve by nas napadlo ukladat ticks ako Int64 - to zafunguje, avsak niekedy chceme mat cas ulozeny ako DateTime (napriklad pri rieseni problemov, ak mame logy (napr transakcii), jazyk SQL a databaza nam umozni komfortne a rychlo vyhladat problemove zaznamy).
2) slo by to za pouzitia Interceptorov, toto riesenie sa mi zda nevhodne, smrdi. Overridnut OnSave, OnFlushDirty...
public override bool OnFlushDirty(object entity,
object id,
object[] currentState,
object[] previousState,
string[] propertyNames,
IType[] types)
{
if(entity is DomainObjectICareAbout)
for ( int i=0; i < propertyNames.Length; i++ ) {
if ( currentState[i] is DateTime ) {
DateTime dt = (DateTime)currentState[i];
dt = dt.AddMilliseconds(-1 * dt.Millisecond);
return true;
}
}
}
return false;
}
3) session.Refresh(entity) by malo zafungovat akurat je to extra query.
4) vlastny typ - implementovat IUserType, co sa mi zda najvhodnejsie riesenie.
Pekny post som nasiel tu: http://www.mostlyclean.com/post/2007/11/Increasing-DateTime-storage-precision-in-Nhibernate-(and-Castle-ActiveRecord).aspx
Z clanku ukazka:
public class PreciseDateTimeUserType : IUserType
{
#region IUserType Members
......
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
int ordinal = rs.GetOrdinal(names[0]);
if (rs.IsDBNull(ordinal))
{
return new NullPreciseDateTime();
}
else
{
long value = rs.GetInt64(ordinal);
return new PreciseDateTime(value);
}
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
long val = ((PreciseDateTime) value);
((IDbDataParameter) cmd.Parameters[index]).Value = val;
}
.....
#endregion
}
Je tam priklad tak pre NH ako aj CastleRecord.