【问题标题】:Fluent NHibernate Problems with SQL Server 2008 DATE Column ValuesSQL Server 2008 DATE 列值的流畅 NHibernate 问题
【发布时间】:2011-03-24 21:13:50
【问题描述】:

您好,

我在使用 Fluent NHibernate 的 C# 中使用 SQL Server 2008 DATE 列时遇到问题。

当我尝试更新在 1753 年 1 月 1 日(DATETIME 的最小日期)之前在不可为空的 DATE 列中具有值的记录时,我收到一条错误消息,指出它可以t 在该列中插入 NULL。如果该值大于 1753 年 1 月 1 日,则没有问题并保留正确的日期值。

这是我的模型文件:

public class Table1 : model.DBObject
{
  public virtual Int32 TestID { get; private set; }
  public virtual String Description { get; set; }
  public virtual DateTime TestDate { get; set; }

  public Table1()
  {
  }

  public static Table1 Load(DBSess sess, Int32 TestID)
  {
      return (Table1)sess.Session.Get(typeof(Table1), TestID);
  }
}

我的映射文件:

public class Table1Map : ClassMap<Table1>
{
  public Table1Map()
  {
    Table("[Table1]");
    Id(x => x.TestID).GeneratedBy.Identity();
    Map(x => x.Description).Not.Nullable();
    Map(x => x.TestDate).Not.Nullable().CustomType("date");
  }
}

正在执行的代码:

using (DBSess sess = DBSess.Create())
{
  Table1 tbl = dal.Table1.Load(sess, 1);
  tbl.Description = String.Format("Updated {0}", DateTime.Now);
  tbl.Save(sess);
  sess.Commit();
}

导出的 NHibernate 映射:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="dal.Table1, dal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="[Table1]">
    <id name="TestID" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="TestID" />
      <generator class="identity" />
    </id>
    <property name="Description" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Description" not-null="true" />
    </property>
    <property name="TestDate" type="date">
      <column name="TestDate" not-null="true" />
    </property>
  </class>
</hibernate-mapping>

log4net 捕获的 NHibernate.SQL 日志条目:

DEBUG2011-03-24 05:00:18 – SELECT table1x0_.TestID as TestID0_0_, table1x0_.Description as Descript2_0_0_, table1x0_.TestDate as TestDate0_0_ FROM [Table1] table1x0_ WHERE table1x0_.TestID=@p0;@p0 = 1
DEBUG2011-03-24 05:00:18 – UPDATE [Table1] SET Description = @p0, TestDate = @p1 WHERE TestID = @p2;@p0 = 'Updated 3/24/2011 5:00:18 PM', @p1 = NULL, @p2 = 1

log4net 捕获的 NHibernate 日志条目的相关部分:

DEBUG2011-03-24 05:00:18 – Building an IDbCommand object for the SqlString: UPDATE [Table1] SET Description = ?, TestDate = ? WHERE TestID = ?
DEBUG2011-03-24 05:00:18 – Dehydrating entity: [dal.Table1#1]
DEBUG2011-03-24 05:00:18 – binding 'Updated 3/24/2011 5:00:18 PM' to parameter: 0
DEBUG2011-03-24 05:00:18 – binding '6/12/1700' to parameter: 1
DEBUG2011-03-24 05:00:18 – binding '1' to parameter: 2
DEBUG2011-03-24 05:00:18 – Obtaining IDbConnection from Driver
ERROR2011-03-24 05:00:19 – Could not execute command: UPDATE [Table1] SET Description = @p0, TestDate = @p1 WHERE TestID = @p2
System.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'TestDate', table 'test2.dbo.Table1'; column does not allow nulls. UPDATE fails.
The statement has been terminated.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)

日志似乎显示值“6/12/1700”正确绑定到 DATE 列的参数,但 SQL 语句抛出异常,说明它正在尝试插入 NULL。如果记录中的值大于“1/1/1753”,则没有例外,并且该值被正确保留。

我可以发布完整的 NHibernate 日志文件,如果那里有更多信息可能会有所帮助。我不确定从这里到哪里寻找答案。

有人对在哪里寻找解决方案有任何想法吗?

提前致谢,

~吉姆·芬内尔

【问题讨论】:

  • 我仍然没有找到任何解释。我开始怀疑这是 NHibernate 中的一个错误。是否有其他人在使用小于最小 DATETIME 值的 SQL 2008 DATE 值时遇到类似问题?如果您成功更新了小于 1/1/1753 的 DATE 值(没有将它们更改为 NULL),您愿意分享我们的映射和模型文件的代码 sn-ps 用于相关列和表吗?
  • 通过进一步的讨论和测试,已经确定这个问题与ADO.NET和NHibernate对MS SQL 2008数据类型DATE的列的DbType.Date的使用有关。如果代码使用 SqlDbType.Date 而不是 DbType.Date,则不会发生此类问题,并且一切都会按指定执行。虽然 DbType.Date 的使用对于其他数据库类型似乎更“可移植”,但它确实引入了这个问题,因为 ADO.NET DbType.Date 的最小值限制为 1/1/1753。

标签: sql-server-2008 exception date fluent-nhibernate


【解决方案1】:

抱歉,我刚刚意识到我应该将其发布为问题的答案,而不是仅仅发表评论......请不要嘲笑“新人!”

通过进一步的讨论和测试,这个问题似乎与 ADO.NET 和 NHibernate 对数据类型 DATE 的 MS SQL 2008 列的 DbType.Date 的使用有关。如果代码使用 SqlDbType.Date 而不是 DbType.Date,则不会发生此类问题,并且一切都会按指定执行。

虽然 DbType.Date 的使用对于其他数据库类型可能看起来更“可移植”,但它确实引入了这个问题,因为 ADO.NET DbType.Date 具有最小值 1/1/1753 的限制。

解决这个问题的方法是告诉 NHibernate 列的类型是 DATETIME2,它也支持早于 1753 年 1 月 1 日的日期值。如果 NHibernate 映射是:

<property name="TestDate" type="datetime2">
  <column name="TestDate" sql-type="date" />
</property>

代码按预期执行,没有异常。

太糟糕了,NHibernate 无法识别它正在使用 MsSql2008 方言并使用 SqlDbTypes 而不是 DbTypes,但这种解决方法目前似乎有效。

有关 ADO.NET 方面的更多信息,请参阅ADO.NET Insert Min Value into SQL Server 2008 Date column crashes。感谢@Graham Bunce 在这方面的帮助。

我希望这些信息对以后遇到此问题的人有所帮助。

【讨论】:

  • 这并不能解决我的问题,针对 SQL Server Express(它实际上是 SQL Server 2008,使用相同的方言等......)。
【解决方案2】:

将 FluentNHibernate 与 NHibernate 2.1 一起使用时,这适用于 SQL Server 2008 R2:

public class TestMap : Map<TestMap> {
    Map(x => x.DateStart).CustomType("datetime2");
}

【讨论】:

  • 有点奇怪,Map(pn =&gt; pn.DateStart).CustomType&lt;DateTime2Type&gt;(); 给了我一个No parameterless constructor defined for this object.,上面的 Ivan 代码对我有用,尽管时间总是 2014-01-13 XX:XX:XX.XXX3843 格式(always 以 3843 结尾)。使用 Timestamp 自定义类型会将最后四位数字清零,因此我不确定最佳组合(CustomType、Timestamp 或 DateTime2,SQL Server 中的哪个列类型(DateTime 或 DateTime2))是什么。
猜你喜欢
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-17
相关资源
最近更新 更多