25

然后我使用 Fluent NHibernate 及其自动映射功能来映射以下简化的 POCO 类:

public class Foo
{    
public virtual int Id { get; set; }    
public virtual datetime CreatedDateTime { get; set; }    
}

默认情况下,CreatedDateTime 字段将映射到 SQL DateTime。但是,如果我进行测试以检查实体是否正确创建,它会失败。这是因为 DateTime 字段的精度没有通过 SQL 数据库来维护。我了解这背后的原因是 MS SQL Server DateTime 只能通过四舍五入到 .000、.003 或 .007 的增量来保持毫秒精度(请参阅http://msdn.microsoft.com/en-us/library /ms187819.aspx)。由于这个原因,NHibernate 会在保存到存储时截断毫秒。这导致我的测试失败,因为我的 .NET DateTime 保存了它的毫秒数,但在保存后恢复的 DateTime 已经丢失了它的毫秒数,因此两者并不真正相等。

为了克服这个问题,我在 Foo 对象中添加了以下映射:

public class FooMap : IAutoMappingOverride<Foo>
{
    public void Override(AutoMapping<Foo> mapping)
    {
        mapping.Map(f => f.CreatedDateTime).CustomType("datetime2");     
    }
}

我知道这种映射使 NHibernate 将 CreatedDateTime 持久化为 datetime2 的 SQL 类型,它可以存储 .NET DateTime 可以存储的完整精度。这是一种享受,现在测试通过了。

然而,一次通过又一次失败:我检查架构导出的测试现在失败,并出现以下错误:

System.ArgumentException : Dialect does not support DbType.DateTime2
Parameter name: typecode

堆栈跟踪:

at NHibernate.Dialect.TypeNames.Get(DbType typecode)
at NHibernate.Dialect.Dialect.GetTypeName(SqlType sqlType)
at NHibernate.Mapping.Column.GetDialectTypeName(Dialect dialect, IMapping mapping)
at NHibernate.Mapping.Table.SqlCreateString(Dialect dialect, IMapping p, String defaultCatalog, String defaultSchema)
at NHibernate.Cfg.Configuration.GenerateSchemaCreationScript(Dialect dialect)
at NHibernate.Tool.hbm2ddl.SchemaExport..ctor(Configuration cfg, IDictionary`2 configProperties)
at NHibernate.Tool.hbm2ddl.SchemaExport..ctor(Configuration cfg)

该代码使用 NHibernate.Tool.hbm2ddl.SchemaExport 对象来调用 Execute 方法。

我正在使用 Fluent v1 和 NHibernate v2.1。

我也尝试将 my 映射DateTime到 TimeStamp 但由于插入失败,甚至无法使映射正常工作:

无法将显式值插入时间戳列。与列列表一起使用INSERT以排除时间戳列,或将 aDEFAULT插入时间戳列。

有谁知道如何让 SchemeExport 与 a 一起使用datetime2或如何让时间戳映射为datetime属性工作?

4

5 回答 5

33

实际上,NHibernate 参考声明 DateTime nhibernate 类型会将 .NET DateTime 存储为在第二级截断的 SQL 日期时间(无毫秒粒度)

因此,它提供了TimestampNHibernate 类型(type="Timestamp"在映射中),它将 .NET 存储为没有截断DateTime的 SQL 。datetime请注意这里不需要 SQL数据timestamp类型,如果您在一个表中有多个列,它实际上会中断。因此,区分 NHibernate 映射中的和属性很重要。timestampsql-typetype

此外,请注意,如果您正在使用过滤器,则过滤器定义中适用相同的规则:如果您指定DateTime参数,则参数的值将在毫秒内被截断。

查看第 5.2.2 章。基本值类型表 5.3 System.ValueType 映射类型

于 2010-02-23T11:56:43.770 回答
6

对于希望实际保留日期的纳秒部分的任何人,您必须使用 DateTime2 作为 sql-column 类型以及 Nhibernate DateTime2 类型。

这是我设置它的约定(使用流利的)

public class DateTimeConvention : IPropertyConvention, IPropertyConventionAcceptance
{

    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Type == typeof(DateTime) || x.Type == typeof(DateTime?));
    }
    public void Apply(IPropertyInstance instance)
    {
        instance.CustomSqlType("DateTime2"); //specify that the sql column is DateTime2
        instance.CustomType("DateTime2"); //set the nhib type as well
    }
}

并激活约定:

 var v = Fluently.Configure()
         .Database(MsSqlConfiguration.MsSql2008
         .ConnectionString(d => d.FromConnectionStringWithKey("connstring"))
         .ShowSql())
         .Mappings(m => m.FluentMappings.AddFromAssemblyOf<IRepository>()
         .Conventions.AddFromAssemblyOf<IRepository>()) //this adds your convention
         .BuildSessionFactory();

使用它,您将在存储日期时间时保持纳秒。

于 2015-06-10T10:00:55.410 回答
1

我在业务类的 CreatedDate 审计字段中遇到了同样的问题。我通过使用实用方法中的值设置时间来解决它。希望这可以帮助。

     /// <summary>
    /// Return a DateTime with millisecond resolution to be used as the timestamp. This is needed so that DateTime of an existing instance
    /// will equal one that has been persisted and returned from the database. Without this, the times differ due to different resolutions.
    /// </summary>
    /// <returns></returns>
    private DateTime GetTime()
    {
        var now = DateTime.Now;
        var ts = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond, DateTimeKind.Local);
        return ts;
    }
于 2010-01-09T14:40:29.533 回答
0

我能够使用以下方法解决我的乐观锁定问题:(使用 datetime2)。

请注意,我使用了此处的名称(以及数据类型名称的大小写):http: //msdn.microsoft.com/en-us/library/system.data.dbtype.aspx “DateTime2”在我的映射代码中(在 CustomType 下)而不是 Sql Server 数据类型案例(“datetime2”)。我不确定这是否会有所不同,但我想指出这一点。

流利的映射:

public class DogBreedMap : ClassMap<DogBreed>
{
    public DogBreedMap()
    {
        Id(x => x.DogBreedUUID).GeneratedBy.GuidComb();
        OptimisticLock.Version();
        Version(x => x.Version)
           .Column("MyTimestamp").CustomType("DateTime2");
    }
}




public partial class DogBreed
{

    public DogBreed()
    {
        CommonConstructor();
    }

    private void CommonConstructor()
    {
        this.Version = DateTime.MinValue; /*I don't think this is necessary*/
    }

    public virtual Guid? DogBreedUUID { get; set; }

    public virtual DateTime Version { get; set; }
}

Sql Server 列创建于:

[MyTimestamp] [datetime2](7) NOT NULL

我的基本测试有效,我(正确地)收到这样的异常(当其他人更新了行时)

行被另一个事务更新或删除(或未保存值映射不正确):[DogBreed#abcabc1d-abc4-abc9-abcb-abca01140a27]

at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows, Object id, Int32 tableNumber, IExpectation expectation, IDbCommand statement)

在 NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session) 在 NHibernate。 Persister.Entity.AbstractEntityPersister.UpdateOrInsert(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session) at NHibernate.Persister.Entity .AbstractEntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session) 在 NHibernate.Action.EntityUpdateAction.Execute() 在 NHibernate .Engine.ActionQueue。在 NHibernate.Engine.ActionQueue.ExecuteActions() 在 NHibernate.Engine.ActionQueue.ExecuteActions(IList 列表) 在 NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource 会话) 在 NHibernate.Event.Default.DefaultFlushEventListener 执行(IExecutable 可执行文件)。 NHibernate.Impl.SessionImpl.Flush() 的 OnFlush(FlushEvent 事件) NHibernate.Transaction.AdoTransaction.Commit()

于 2010-11-26T21:52:04.283 回答
0

在我的域中,从 SQL Server 中的日期时间丢失毫秒是可以接受的。因此,我允许我的持久性测试人员使用这个静态助手(nunit 实现)进行容差:

public static class AssertDateTime
{
    /// <summary>
    /// Checks that the DateTimes are no more than second apart
    /// </summary>
    /// <param name="Expected"></param>
    /// <param name="Actual"></param>
    public static void AreWithinOneSecondOfEachOther(DateTime Expected, DateTime Actual)
    {
        var timespanBetween = Actual.Subtract(Expected);

        if (timespanBetween > TimeSpan.FromSeconds(1))
            Assert.Fail(string.Format("The times were more than a second appart. They were out by {0}. Expected {1}, Actual {2}.", timespanBetween, Expected, Actual));
    }
}
于 2010-02-23T11:25:33.257 回答