如何在Sql Server 2008/2012中创建一段时间的NHibernate IUserType?

如何在Sql Server 2008/2012中创建一段时间的NHibernate IUserType?,nhibernate,nodatime,Nhibernate,Nodatime,我正在尝试为Noda TimeLocalTime类型创建一个NHibernateIUserType,该类型将逻辑映射到Sql Server 2008/2012中的Time类型。我能够从数据库中保存和加载值。但是,我无法编写包含本地时间比较的查询,如\u session.Query()。其中(x=>x.Start=Start)给出错误SqlException(0x80131904):数据类型time和datetime在小于运算符中不兼容。 “我的用户类型”中的相关代码为: 公共类型返回的类型 {

我正在尝试为Noda Time
LocalTime
类型创建一个NHibernate
IUserType
,该类型将逻辑映射到Sql Server 2008/2012中的
Time
类型。我能够从数据库中保存和加载值。但是,我无法编写包含本地时间比较的查询,如
\u session.Query()。其中(x=>x.Start=Start)
给出错误
SqlException(0x80131904):数据类型time和datetime在小于运算符中不兼容。

“我的用户类型”中的相关代码为:

公共类型返回的类型
{
获取{return typeof(LocalTime);}
}
公共重写对象NullSafeGet(IDataReader rs,字符串[]名称,对象所有者)
{
var dbValue=NHibernateUtil.Time.NullSafeGet(rs,名称);
if(dbValue==null)
返回null;
返回LocalDateTime.FromDateTime((DateTime)dbValue).TimeOfDay;
}
public override void NullSafeSet(IDbCommand cmd,对象值,int索引)
{
如果(值==null)
NHibernateUtil.Time.NullSafeSet(cmd,null,index);
其他的
NHibernateUtil.Time.NullSafeSet(cmd,((LocalTime)值).LocalDateTime.ToDateTimeUnspecified(),索引);
}
公共重写SqlType[]SqlTypes
{
获取{returnnew[]{SqlTypeFactory.Time};}
}
问题在于,尽管上面的代码指示数据库类型为时间,但它会生成以下查询(根据Sql探查器):

exec sp_executesql N'从[SchedulingTemplate]scheduling0'中选择[…],其中scheduling0.Start=@p1',N'@p0-datetime,@p1-datetime',@p0='1753-01-01 20:00:00',@p1='1753-01-01 06:00:00'

(注:为简洁起见,我省略了选择列表)

请注意,参数的类型和值被视为
datetime

这似乎非常类似于两个NH bug,它们已经被关闭并关闭

我尝试使用
NHibernateUtil.TimeAsTimeSpan
,但似乎也不起作用。它生成了完全相同的查询,这让我感到惊讶。我在想,NH-2661中描述的问题可能也存在于用户类型中,并且没有解决这个问题


我正在使用NHibernate v3.3.1.400和Noda Time 1.0.0-beta2

按照@Firo的建议,我从SqlType开始工作,并得出以下结论:

使用NHibernate;
使用NHibernate方言;
使用NHibernate.SqlTypes;
使用NHibernate.Type;
使用NodaTime;
使用NodaTime.Text;
使用制度;
使用系统数据;
使用System.Data.SqlClient;
[可序列化]
公共类LocalTimeType:PrimitiveType,IIIdentifierType
{
私有只读LocalTimePattern\u timePattern=LocalTimePattern.CreateWithInvariantCulture(“h:mm:ss tt”);
public LocalTimeType():base(SqlTypeFactory.Time){}
公共重写字符串名
{
获取{返回“LocalTime”;}
}
公共重写对象Get(IDataReader rs,int索引)
{
尝试
{
if(rs[index]是TimeSpan)//对于DbType.Time表示TimeSpan的方言。
{
风险值时间=(时间跨度)rs[指数];
返回LocalTime.Midnight+Period.FromTicks(time.Ticks);
}
var dbValue=Convert.ToDateTime(rs[index]);
返回LocalDateTime.FromDateTime(dbValue).TimeOfDay;
}
捕获(例外情况除外)
{
抛出新的FormatException(string.Format(“输入字符串“{0}”的格式不正确。”,rs[index]),ex);
}
}
公共重写对象Get(IDataReader rs,字符串名称)
{
返回Get(rs,rs.GetOrdinal(name));
}
公共重写类型ReturnedClass
{
获取{return typeof(LocalTime);}
}
公共覆盖无效集(IDBST命令、对象值、int索引)
{
var参数=((SqlParameter)st.Parameters[index]);
parameter.SqlDbType=SqlDbType.Time;//破解坏行为,M$表示不理想,但正如预期的那样,NH表示这是MS中的一个bug,最终可能会解决
parameter.Value=newtimespan(((LocalTime)Value).TickOfDay);
}
公共覆盖布尔相等(对象x、对象y)
{
返回等于(x,y);
}
公共覆盖int GetHashCode(对象x,EntityMode EntityMode)
{
返回x.GetHashCode();
}
公共重写字符串到字符串(对象值)
{
返回_timePattern.Format((LocalTime)val);
}
公共对象StringToObject(字符串xml)
{
返回字符串.IsNullOrEmpty(xml)?null:FromStringValue(xml);
}
公共重写对象FromStringValue(字符串xml)
{
返回\u timePattern.Parse(xml).Value;
}
公共重写类型基元类
{
获取{return typeof(LocalTime);}
}
公共覆盖对象默认值
{
获取{返回新的LocalTime();}
}
公共重写字符串ObjectToSQLString(对象值,方言)
{
返回“'”+_timePattern.Format((LocalTime)值)+“'”;
}
}
键代码在
集合
方法中,其中表示:

var parameter = ((SqlParameter)st.Parameters[index]);
parameter.SqlDbType = SqlDbType.Time;

这是必需的,因为MS数据提供程序将
DbType
设置为
DbType.Time
意味着基础类型应为
DateTime
必须将
SqlDbType
设置为工作时间。

在解决此问题之前,您必须实现自己的SqlType。从TimeType复制代码并修复它。从
TimeSpan
转换为
LocalTime
应该比现在干净得多。我为此提出了第148号问题。从
DateTime
转换为
LocalTime
比显示的要简单-只需通过
LocalDateTime
LocalTime time=LocalDateTime.FromDateTime(DateTime).TimeOfDay谢谢@JonSkeet我已更改为使用
LocalDateTime.FromDateTime