本文介绍了使用NHibernate保存和加载Utc DateTime的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在将DateTime保存到SQL Lite数据库时遇到问题。 (也许也可以使用MS SQL)



我想使用NHibernate将UTC时间中的DateTime保存到数据库中并从数据库中加载它。我们使用UTC时间在Hole应用程序中工作,当我们在ui上显示时间时,我们将其更改为本地时间。



我了解了很多有关DateTime和NHibernate的信息:










但是没什么g有效。
一些示例:



PreUpdate :是在保存实体之前。



已保存:是使用nhibernate保存后保存的对象(r​​epo.save(o);)。



已加载:当我通过ID从存储库加载实体时。

  //接下来的3个示例是:
o.Created = DateTime.UtcNow;



映射类型: CustomType< TimestampType>()



UtcTime:16:44 ... LocalTime:18:44




  • PreUpdate:2015-03-30T16:44:35.7636679Z Tick:635633306757636679 Kind:Utc

  • Saved:2015-03-30T16:44:35.7636679Z Tick:635633306757636679 Kind:Utc

  • 已加载:2015-03-30T18:44:35.7636679刻度线:635633378757636679种类:未指定



问题这里是,当我通过id重新加载对象时,新对象的时间为18 ...(+ 2h)而不是16。...并且DateTime类型未指定。



映射类型: CustomType< DateTimeType>()



UtcTime:16:49 ... LocalTime: 18:49




  • 预更新:2015-03-30T16:49:00.2754289Z勾号:635633309402754289种类:UTC

  • 保存:2015-03-30T16:49:00.2754289Z刻度:635633309402754289种类:Utc

  • 已加载:2015-03-30T16:49:00.0000000刻度:635633309400000000种类:未指定



通过这种解决方案,我松开了毫秒,并且DateTime类型也未指定。



映射类型: CustomType< UtcDateTimeType>()



UtcTime:17:01。 。LocalTime:19:01




  • 预更新:2015-03-30T17:01:32.9663859Z订单号:635633316929663859类型:UTC

  • 保存:2015-03-30T17:01:32.9663859Z勾号:635633316929663859种类:UTC

  • 已加载:2015-03-30T19:01:32.0000000Z勾号: 635633388920000000 Kind:Utc



使用此解决方案,我松开了毫秒,DateTime类型是utc,但它是错误的时间,应该是17:01 ....



所以另一个想法是在应用程序中仅使用DateTime.Now并将utc时间保存在数据库中。例如:

  //接下来的三个示例是:
o.Created = DateTime.Now;



映射类型: CustomType< TimestampType>()



UtcTime:17:21 ... LocalTime:19:21




  • PreUpdate:2015-03-30T19:21:44.7938077 + 02:00勾号:635633401047938077种类:本地

  • 保存:2015-03-30T19:21:44.7938077 + 02:00勾号:635633401047938077种类:本地

  • 已加载:2015-03-30T19:21:44.7938077滴答:635633401047938077种类:未指定



使用这种解决方案,我有毫秒数,未指定DateTime类型,并且加载的时间不是utc。



映射类型: CustomType< ; DateTimeType>()



UtcTime:17:19 ... LocalTime:19:19




  • 预更新:2015-03-30T19:19:27.3114047 + 02:00刻度线:635633399673114047种类:本地

  • 保存:2015-03- 30T19:19:27.3114047 + 02:00勾号:635633399673114047种类:本地

  • 已加载:2015-03-30T19:19:27.0000000刻度:635633399670000000种类:Unspe cified



通过这种解决方案,我松开了毫秒,DateTime类型也未指定,并且加载时间不是utc。



映射类型: CustomType< UtcDateTimeType>()



UtcTime: 17:14 ... LocalTime:19:14




  • 预更新:2015-03-30T19:14:31.3030033 + 02:00滴答: 635633396713030033类型:本地

  • 已保存:2015-03-30T19:14:31.3030033 + 02:00刻度线:635633396713030033类型:本地

  • 已加载:2015 -03-30T21:14:31.0000000Z Tick:635633468710000000 Kind:Utc



使用这种解决方案,我可以放慢毫秒,DateTime类型是utc,但是时间错误,应该是17:14 ....



所以我有一些疑问:


  1. 为什么NHibernate加载本地时间但使用utc类型(UtcDateTimeType和o.Created = DateTime.UtcNow)

  2. 使用起来更好吗在Hole应用程序和UI localtime中使用utc或使用localt


我还创建了一个自己的映射:

 命名空间Persistence.Common.NHibernate 
{
使用系统;
使用System.Data;

使用global :: NHibernate.Engine;
使用global :: NHibernate.Type;

///< summary>
///这与DateTime几乎完全相同,但可以在$ version列中使用
///,将其存储到数据库支持的精度,
///如果值为null,则默认为DateTime.Now的值。
///< / summary>
///<备注>
///< p>
///存储在数据库中的值取决于您的数据提供者能够存储的数据
///。因此,您保存的DateTime有可能不是
///与您在检查DateTime.Equals(DateTime)时返回的相同DateTime相同,因为
///将关闭毫秒数。
///< / p>
///< p>
///例如-SQL Server 2000仅精确到3.33毫秒。因此,如果
/// NHibernate写入值< c> 01/01/98 23:59:59.995< / c>到准备好的命令,MsSql
///将其存储为< c> 1998-01-01 23:59:59.997< / c>。
///< / p>
///< p>
///请检查数据库服务器的文档。
///< / p>
///< /备注>
[可序列化]
公共类CustomUtcTimestampType:TimestampType
{
public CustomUtcTimestampType()
{
}

公共替代对象Get(IDataReader rs,int index)
{
return Convert.ToDateTime(rs [index])。ToLocalTime();
}

///< summary>
///在IDbCommand中设置此Type的值。
///< / summary>
///< param name = st>用于将Type值添加到的IDbCommand。< / param>
///< param name = value>类型的值。< / param>
///< param name = index> IDbCommand中IDataParameter的索引。
///<备注>
///不会将此类型的null值写入IDbCommand。
///< / remarks>
公共重写void Set(IDbCommand st,object value,int index)
{
DateTime dateTime =(DateTime)((value is DateTime)?value:DateTime.UtcNow);
dateTime = DateTime.SpecifyKind(dateTime.ToUniversalTime(),DateTimeKind.Unspecified);
((IDataParameter)st.Parameters [index])。Value = dateTime;
}

公共重写字符串名称
{
get {return CustomUtcTimestamp; }
}

公共替代对象FromStringValue(string xml)
{
return DateTime.Parse(xml);
}

#region IVersionType成员

公共重写对象Seed(ISessionImplementor session)
{
if(session == null)
{
返回DateTime.UtcNow;
}
返回Round(DateTime.UtcNow,session.Factory.Dialect.TimestampResolutionInTicks);
}

#endregion

公共对象StringToObject(string xml)
{
return DateTime.Parse(xml);
}

公共替代字符串ObjectToSQLString(对象值,全局:: NHibernate.Dialect.Dialect方言)
{
return'\''+ value.ToString ()+'\'';
}
}
}


解决方案

谢谢您的回答。在您使用自定义类型的版本时,加载日期时间通常会出现错误的时间。问题是此代码 base.Get(rs,index)。当我执行此方法时,它将utc时间从数据库转换为错误的DateTime。我现在的解决方案是,将时间保存在utc中,但是在将DateTimeKind更改为Unspecified并将值存储在数据库(SQLite)中之前。结果是最后没有Z的时间。加载时间时,我在加载DateTime之后更改了DateTimeKind,一切正常。



以下代码解决了我的问题:

 使用系统; 
使用System.Data;

使用global :: NHibernate.Engine;
使用global :: NHibernate.Type;

使用Foundation.Core;

///< summary>
///此类型保存< see cref = DateTime />到数据库。您需要保存< see cref = DateTime />以UTC(<参见cref = DateTimeKind.Utc />)。
///加载< see cref = DateTime />时,时间以UTC为单位。
///< / summary>
///<另请参见cref = http://stackoverflow.com/questions/29352719/save-and-load-utc-datetime-with-nhibernate />
公共类UtcTimestampType:TimestampType
{
公共替代字符串名称
{
get {返回 UtcTimestamp; }
}

///< summary>
///在IDbCommand中设置此Type的值。
///< / summary>
///< param name = st>用于将Type值添加到的IDbCommand。< / param>
///< param name = value>类型的值。< / param>
///< param name = index> IDbCommand中IDataParameter的索引。
///<备注>
///不会将此类型的null值写入IDbCommand。
///< see cref = DateTime.Kind />必须为<参见cref = DateTimeKind.Utc />。
///< / remarks>
公共重写void Set(IDbCommand st,object value,int index)
{
DateTime dateTime =(DateTime)((value is DateTime)?value:DateTime.UtcNow);
Check.IsValid(()=> dateTime,dateTime,time => time.Kind == DateTimeKind.Utc,您需要以utc格式保存日期时间。);
//将类型更改为未指定,因为在加载日期时间时,我们使用utc类型的值有误。
((IDataParameter)st.Parameters [index])。Value = DateTime.SpecifyKind(dateTime,DateTimeKind.Unspecified);
}

公共替代对象Get(IDataReader rs,int index)
{
return ChangeDateTimeKindToUtc(base.Get(rs,index));
}

公共重写对象Get(IDataReader rs,字符串名称)
{
return ChangeDateTimeKindToUtc(base.Get(rs,name));
}

公共替代对象FromStringValue(string xml)
{
return ChangeDateTimeKindToUtc(base.FromStringValue(xml));
}

公共替代对象Seed(ISessionImplementor会话)
{
if(session == null)
{
返回DateTime.UtcNow ;
}

return Round(DateTime.UtcNow,session.Factory.Dialect.TimestampResolutionInTicks);
}

Private DateTime ChangeDateTimeKindToUtc(object value)
{
DateTime dateTime =(DateTime)value;
返回新的DateTime(dateTime.Ticks,DateTimeKind.Utc);
}
}

您对这两种方法有何看法?我需要这些吗?我应该使用 Round(DateTime.UtcNow ... 吗?何时需要 FromStringValue

 公共替代对象FromStringValue(string xml)
{
return ChangeDateTimeKindToUtc(base.FromStringValue(xml));
}

公共替代对象Seed(ISessionImplementor会话)
{
if(session == null)
{
返回DateTime.UtcNow;
}

返回Round(DateTime.UtcNow,session.Factory.Dialect.TimestampResolutionInTicks);
}


I have problems with saving DateTime to an SQL Lite data base. (perhaps also with MS SQL)

I want to save a DateTime in UTC time with NHibernate to the database and load it from the database. We work in the hole application with utc time and when we present the time on the ui, we change it to local time.

I read a lot about DateTime and NHibernate:

But nothing works.Some expample:

PreUpdate: is before saving the entity.

Saved: is the saved object after saving with nhibernate (repo.save(o);).

Loaded: when I load the entity from the repository by id.

// The next 3 examples are with:
o.Created = DateTime.UtcNow;

Mappingtype: CustomType<TimestampType>()

UtcTime: 16:44... LocalTime: 18:44

  • PreUpdate: 2015-03-30T16:44:35.7636679Z Tick: 635633306757636679 Kind:Utc
  • Saved: 2015-03-30T16:44:35.7636679Z Tick: 635633306757636679 Kind:Utc
  • Loaded: 2015-03-30T18:44:35.7636679 Tick: 635633378757636679 Kind:Unspecified

The problem here is, when I reload the object by id, the new object has the time 18... (+2h) instead of 16.... and the DateTime kind is Unspecified.

Mappingtype: CustomType<DateTimeType>()

UtcTime: 16:49... LocalTime: 18:49

  • PreUpdate: 2015-03-30T16:49:00.2754289Z Tick: 635633309402754289 Kind:Utc
  • Saved: 2015-03-30T16:49:00.2754289Z Tick: 635633309402754289 Kind:Utc
  • Loaded: 2015-03-30T16:49:00.0000000 Tick: 635633309400000000 Kind:Unspecified

With this solution, I loose the milliseconds and the DateTime kind is also Unspecified.

Mappingtype: CustomType<UtcDateTimeType>()

UtcTime: 17:01... LocalTime: 19:01

  • PreUpdate: 2015-03-30T17:01:32.9663859Z Tick: 635633316929663859 Kind:Utc
  • Saved: 2015-03-30T17:01:32.9663859Z Tick: 635633316929663859 Kind:Utc
  • Loaded: 2015-03-30T19:01:32.0000000Z Tick: 635633388920000000 Kind:Utc

With this solution, I loose the milliseconds, the DateTime kind is utc but it is the wrong time, it should be 17:01....

So another idea was to use in the application only DateTime.Now and save the utc time in the database. Some example:

// The next 3 examples are with:
o.Created = DateTime.Now;

Mappingtype: CustomType<TimestampType>()

UtcTime: 17:21... LocalTime: 19:21

  • PreUpdate: 2015-03-30T19:21:44.7938077+02:00 Tick: 635633401047938077 Kind:Local
  • Saved: 2015-03-30T19:21:44.7938077+02:00 Tick: 635633401047938077 Kind:Local
  • Loaded: 2015-03-30T19:21:44.7938077 Tick: 635633401047938077 Kind:Unspecified

With this solution, I have the milliseconds, the DateTime kind is Unspecified and the loaded time is not utc.

Mappingtype: CustomType<DateTimeType>()

UtcTime: 17:19... LocalTime: 19:19

  • PreUpdate: 2015-03-30T19:19:27.3114047+02:00 Tick: 635633399673114047 Kind:Local
  • Saved: 2015-03-30T19:19:27.3114047+02:00 Tick: 635633399673114047 Kind:Local
  • Loaded: 2015-03-30T19:19:27.0000000 Tick: 635633399670000000 Kind:Unspecified

With this solution, I loose the milliseconds, the DateTime kind is also Unspecified and the loaded time is not utc.

Mappingtype: CustomType<UtcDateTimeType>()

UtcTime: 17:14... LocalTime: 19:14

  • PreUpdate: 2015-03-30T19:14:31.3030033+02:00 Tick: 635633396713030033 Kind:Local
  • Saved: 2015-03-30T19:14:31.3030033+02:00 Tick: 635633396713030033 Kind:Local
  • Loaded: 2015-03-30T21:14:31.0000000Z Tick: 635633468710000000 Kind:Utc

With this solution, I loose the milliseconds, the DateTime kind is utc but it is the wrong time, it should be 17:14....

So I have some questions:

  1. Why does NHibernate loads the local time but with kind utc (UtcDateTimeType and o.Created=DateTime.UtcNow)
  2. Is it better to use utc in the hole application and in UI localtime or use localtime everywhere and save the time utc at the database.

I've also created an own mapping:

namespace Persistence.Common.NHibernate
{
    using System;
    using System.Data;

    using global::NHibernate.Engine;
    using global::NHibernate.Type;

    /// <summary>
    /// This is almost the exact same type as the DateTime except it can be used
    /// in the version column, stores it to the accuracy the database supports,
    /// and will default to the value of DateTime.Now if the value is null.
    /// </summary>
    /// <remarks>
    /// <p>
    /// The value stored in the database depends on what your data provider is capable
    /// of storing.  So there is a possibility that the DateTime you save will not be
    /// the same DateTime you get back when you check DateTime.Equals(DateTime) because
    /// they will have their milliseconds off.
    /// </p>
    /// <p>
    /// For example - SQL Server 2000 is only accurate to 3.33 milliseconds.  So if
    /// NHibernate writes a value of <c>01/01/98 23:59:59.995</c> to the Prepared Command, MsSql
    /// will store it as <c>1998-01-01 23:59:59.997</c>.
    /// </p>
    /// <p>
    /// Please review the documentation of your Database server.
    /// </p>
    /// </remarks>
    [Serializable]
    public class CustomUtcTimestampType : TimestampType
    {
        public CustomUtcTimestampType()
        {
        }

        public override object Get(IDataReader rs, int index)
        {
            return Convert.ToDateTime(rs[index]).ToLocalTime();
        }

        /// <summary>
        /// Sets the value of this Type in the IDbCommand.
        /// </summary>
        /// <param name="st">The IDbCommand to add the Type's value to.</param>
        /// <param name="value">The value of the Type.</param>
        /// <param name="index">The index of the IDataParameter in the IDbCommand.</param>
        /// <remarks>
        /// No null values will be written to the IDbCommand for this Type.
        /// </remarks>
        public override void Set(IDbCommand st, object value, int index)
        {
            DateTime dateTime = (DateTime)((value is DateTime) ? value : DateTime.UtcNow);
            dateTime = DateTime.SpecifyKind(dateTime.ToUniversalTime(), DateTimeKind.Unspecified);
            ((IDataParameter)st.Parameters[index]).Value = dateTime;
        }

        public override string Name
        {
            get { return "CustomUtcTimestamp"; }
        }

        public override object FromStringValue(string xml)
        {
            return DateTime.Parse(xml);
        }

        #region IVersionType Members

        public override object Seed(ISessionImplementor session)
        {
            if (session == null)
            {
                return DateTime.UtcNow;
            }
            return Round(DateTime.UtcNow, session.Factory.Dialect.TimestampResolutionInTicks);
        }

        #endregion

        public object StringToObject(string xml)
        {
            return DateTime.Parse(xml);
        }

        public override string ObjectToSQLString(object value, global::NHibernate.Dialect.Dialect dialect)
        {
            return '\'' + value.ToString() + '\'';
        }
    }
}
解决方案

Thank you for your answer. With your version of the custom type, I had usually a wrong time when I've loaded the datetime. The problem is this code base.Get(rs, index). When I execute this method, it converts the utc time from the database to a wrong DateTime. My solution is now, to save the time in utc, but before I change the DateTimeKind to Unspecified and store the value in the data base (SQLite). The result is a time without Z at the end. When I load the time, I change the DateTimeKind after loading the DateTime and everything works.

The following code solve my problems:

using System;
using System.Data;

using global::NHibernate.Engine;
using global::NHibernate.Type;

using Foundation.Core;

/// <summary>
/// This type save the <see cref="DateTime"/> to the database. You need to save the <see cref="DateTime"/> in UTC (<see cref="DateTimeKind.Utc"/>).
/// When you load the <see cref="DateTime"/>, then time is in UTC.
/// </summary>
/// <seealso cref="http://stackoverflow.com/questions/29352719/save-and-load-utc-datetime-with-nhibernate"/>
public class UtcTimestampType : TimestampType
{
    public override string Name
    {
        get { return "UtcTimestamp"; }
    }

    /// <summary>
    /// Sets the value of this Type in the IDbCommand.
    /// </summary>
    /// <param name="st">The IDbCommand to add the Type's value to.</param>
    /// <param name="value">The value of the Type.</param>
    /// <param name="index">The index of the IDataParameter in the IDbCommand.</param>
    /// <remarks>
    /// No null values will be written to the IDbCommand for this Type.
    /// The <see cref="DateTime.Kind"/> must be <see cref="DateTimeKind.Utc"/>.
    /// </remarks>
    public override void Set(IDbCommand st, object value, int index)
    {
        DateTime dateTime = (DateTime)((value is DateTime) ? value : DateTime.UtcNow);
        Check.IsValid(() => dateTime, dateTime, time => time.Kind == DateTimeKind.Utc, "You need to save the date time in the utc format.");
        // Change the kind to unspecified, because when we load the datetime we have wrong values with kind utc.
        ((IDataParameter)st.Parameters[index]).Value = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
    }

    public override object Get(IDataReader rs, int index)
    {
        return ChangeDateTimeKindToUtc(base.Get(rs, index));
    }

    public override object Get(IDataReader rs, string name)
    {
        return ChangeDateTimeKindToUtc(base.Get(rs, name));
    }

    public override object FromStringValue(string xml)
    {
        return ChangeDateTimeKindToUtc(base.FromStringValue(xml));
    }

    public override object Seed(ISessionImplementor session)
    {
        if (session == null)
        {
            return DateTime.UtcNow;
        }

        return Round(DateTime.UtcNow, session.Factory.Dialect.TimestampResolutionInTicks);
    }

    private DateTime ChangeDateTimeKindToUtc(object value)
    {
        DateTime dateTime = (DateTime)value;
        return new DateTime(dateTime.Ticks, DateTimeKind.Utc);
    }
}

What do you think about these 2 Methods? Do I need these? Should I use Round(DateTime.UtcNow...? When is FromStringValue needed?

public override object FromStringValue(string xml)
    {
        return ChangeDateTimeKindToUtc(base.FromStringValue(xml));
    }

    public override object Seed(ISessionImplementor session)
    {
        if (session == null)
        {
            return DateTime.UtcNow;
        }

        return Round(DateTime.UtcNow, session.Factory.Dialect.TimestampResolutionInTicks);
    }

这篇关于使用NHibernate保存和加载Utc DateTime的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 11:30