您可以创建一个实现既IPreUpdateEventListener
和IPreInsertEventListener
如下一类:
public class InsertUpdateListener : IPreInsertEventListener, IPreUpdateEventListener {
public bool OnPreInsert(PreInsertEvent @event) {
CheckDateTimeWithinSqlRange(@event.Persister, @event.State);
return false;
}
public bool OnPreUpdate(PreUpdateEvent @event) {
CheckDateTimeWithinSqlRange(@event.Persister, @event.State);
return false;
}
private static void CheckDateTimeWithinSqlRange(IEntityPersister persister, IReadOnlyList<object> state) {
var rgnMin = System.Data.SqlTypes.SqlDateTime.MinValue.Value;
// There is a small but relevant difference between DateTime.MaxValue and SqlDateTime.MaxValue.
// DateTime.MaxValue is bigger than SqlDateTime.MaxValue but still within the valid range of
// values for SQL Server. Therefore we test against DateTime.MaxValue and not against
// SqlDateTime.MaxValue. [Manfred, 04jul2017]
//var rgnMax = System.Data.SqlTypes.SqlDateTime.MaxValue.Value;
var rgnMax = DateTime.MaxValue;
for (var i = 0; i < state.Count; i++) {
if (state[i] != null
&& state[i] is DateTime) {
var value = (DateTime)state[i];
if (value < rgnMin /*|| value > rgnMax*/) { // we don't check max as SQL Server is happy with DateTime.MaxValue [Manfred, 04jul2017]
throw new ArgumentOutOfRangeException(persister.PropertyNames[i], value,
$"Property '{persister.PropertyNames[i]}' for class '{persister.EntityName}' must be between {rgnMin:s} and {rgnMax:s} but was {value:s}");
}
}
}
}
}
您还需要在配置会话工厂然后注册此事件处理程序。在创建NHibernate的会话工厂时,将实例添加到Configuration.EventListeners.PreUpdateEventListeners
和Configuration.EventListeners.PreInsertEventListeners
,然后使用Configuration
对象。
这是干什么的:每当NHibernate插入或更新实体时,它将分别调用OnPreInsert()
或OnPreUpdate()
。每种方法都会调用CheckDateTimeWithinSqlRange()
。
CheckDateTimeWithinSqlRange()
遍历实体的所有属性值,即对象,正被保存。如果属性值不为空,则会检查它是否为DateTime
。如果是这种情况,它会检查它是否不小于SqlDateTime.MinValue.Value
(请注意额外的.Value
以避免例外)。如果您使用的是SQL Server 2012或更高版本,则无需检查SqlDateTime.MaxValue.Value
。他们会高兴地接受,即使是DateTime.MaxValue
,这是几次大于SqlDateTime.MaxValue.Value
。
如果该值超出允许的范围,则此代码将引发带有适当消息的ArgumentOutOfRangeException
,该消息包含导致问题的类(实体)和属性的名称以及传入的实际值。该消息与SqlDateTime溢出异常的等效SqlServerException
类似,但可以更容易查明问题。
一些事情要考虑。显然这不是免费的。由于此逻辑消耗CPU,因此会产生运行时间开销。根据你的情况,这可能不成问题。如果是这样,您还可以考虑优化此示例中给出的代码以使其更快。一种选择可能是使用缓存来避免同一类的循环。另一种选择可能是仅在测试和开发环境中使用它。对于生产,您可以依靠系统的其他部分正常运行,并且这些值始终在有效范围内。
另外,请注意,此代码引入了对SQL Server的依赖关系。 NHibernate通常用于避免这样的依赖。 NHibernate支持的其他数据库服务器可能有不同的datetime允许值范围。同样,还有解决这个问题的选项,例如通过根据SQL方言使用不同的边界。
快乐编码!
请粘贴从应用程序发送到服务器的SQL,如果您没有nhprof或使用log4net,则可以使用SQL事件探查器 – Rippo 2011-02-01 18:42:05