然后我使用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。
我还尝试将我的DateTime
映射到TimeStamp,但由于插入失败说明,甚至无法使映射正常工作:
无法将显式值插入时间戳列。将INSERT
与列列表一起使用以排除时间戳列,或将DEFAULT
插入时间戳列。
有没有人知道如何让SchemeExport使用datetime2
或如何让时间戳映射适用于datetime
属性?
答案 0 :(得分:32)
实际上NHibernate引用声明DateTime nhibernate类型将.NET DateTime存储为在第二级截断的SQL日期时间(无毫秒粒度)
因此它在映射中提供Timestamp
NHibernate类型(type="Timestamp"
),它将.NET DateTime
存储为SQL datetime
而不截断。请注意,SQL timestamp
数据类型需要不,如果一个表中有多个timestamp
列,则会因此而中断。因此,区分NHibernate映射中的sql-type
和type
属性非常重要。
此外,请注意,如果您正在使用过滤器,则在过滤器定义中应用相同的规则:如果指定DateTime
参数,则参数的值将被截断而不会毫秒。
结帐chapter 5.2.2. Basic value types,Table 5.3 System.ValueType Mapping Types。
答案 1 :(得分:5)
对于任何想要实际保留日期的纳秒部分的人,您必须使用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();
使用此功能,您可以在存储日期时保持纳秒。
答案 2 :(得分: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;
}
答案 3 :(得分: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));
}
}
答案 4 :(得分:0)
我能够使用以下方法解决我的乐观锁定问题: (使用datetime2)。
注意,我从这里使用了名称(以及datatype-name的大小写): http://msdn.microsoft.com/en-us/library/system.data.dbtype.aspx “DateTime2”位于我的映射代码中(在CustomType下),而不是Sql Server数据类型大小写(“datetime2”)。我不确定这是否有所作为,但我想指出它。
Fluent Mapping:
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)
at 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) at 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.Execute(IExecutable可执行文件) 在NHibernate.Engine.ActionQueue.ExecuteActions(IList列表) 在NHibernate.Engine.ActionQueue.ExecuteActions() 在NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource会话) 在NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent事件) 在NHibernate.Impl.SessionImpl.Flush() 在NHibernate.Transaction.AdoTransaction.Commit()