Skippov blog

Pre Vas. Pre mna. Pre dalsie generacie.

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.

Bookmark and Share