C# NHibernate Session.Flush() 在没有发生更新时发送更新查询
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34852/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
NHibernate Session.Flush() Sending Update Queries When No Update Has Occurred
提问by Mark Struzinski
I have an NHibernate session. In this session, I am performing exactly 1 operation, which is to run this code to get a list:
我有一个 NHibernate 会话。在这个会话中,我正好执行了 1 个操作,即运行此代码以获取列表:
public IList<Customer> GetCustomerByFirstName(string customerFirstName)
{
return _session.CreateCriteria(typeof(Customer))
.Add(new NHibernate.Expression.EqExpression("FirstName", customerFirstName))
.List<Customer>();
}
I am calling Session.Flush()
at the end of the HttpRequest
, and I get a HibernateAdoException
. NHibernate is passing an update statement to the db, and causing a foreign key violation. If I don't run the flush
, the request completes with no problem. The issue here is that I need the flush in place in case there is a change that occurs within other sessions, since this code is reused in other areas. Is there another configuration setting I might be missing?
我Session.Flush()
在 结束时打电话HttpRequest
,我得到一个HibernateAdoException
. NHibernate 将更新语句传递给数据库,并导致外键冲突。如果我不运行flush
,则请求将毫无问题地完成。这里的问题是,如果其他会话中发生更改,我需要适当的刷新,因为此代码在其他区域重复使用。我可能缺少其他配置设置吗?
Here's the code from the exception:
这是异常中的代码:
[SQL: UPDATE CUSTOMER SET first_name = ?, last_name = ?, strategy_code_1 = ?, strategy_code_2 = ?, strategy_code_3 = ?, dts_import = ?, account_cycle_code = ?, bucket = ?, collector_code = ?, days_delinquent_count = ?, external_status_code = ?, principal_balance_amount = ?, total_min_pay_due = ?, current_balance = ?, amount_delinquent = ?, current_min_pay_due = ?, bucket_1 = ?, bucket_2 = ?, bucket_3 = ?, bucket_4 = ?, bucket_5 = ?, bucket_6 = ?, bucket_7 = ? WHERE customer_account_id = ?]
No parameters are showing as being passed.
没有参数显示为正在传递。
采纳答案by Ryan Duffield
I have seen this once before when one of my models was not mapped correctly (wasn't using nullable types correctly). May you please paste your model and mapping?
当我的模型之一未正确映射(未正确使用可空类型)时,我曾经见过一次。你可以粘贴你的模型和映射吗?
回答by Richard Dingwall
I also experienced this problem in NH 2.0.1 when trying to hide the inverse ends of many-to-many bags using access="noop" (hint: this doesn't work).
我在 NH 2.0.1 中尝试使用 access="noop" 隐藏多对多包的反向端时也遇到了这个问题(提示:这不起作用)。
Converting them to access="field" + adding a field on the class fixed the problem. Pretty hard to track them down though.
将它们转换为 access="field" + 在类上添加一个字段解决了这个问题。虽然很难追踪到他们。
回答by zvolkov
Always be careful with NULLable fields whenever you deal with NHibernate. If your field is NULLable in DB, make sure corresponding .NET class uses Nullable type too. Otherwise, all kinds of weird things will happen. The symptom is usually will be that NHibernate will try to update the record in DB, even though you have not changed any fields since you read the entity from the database.
每当您处理 NHibernate 时,请始终小心 NULLable 字段。如果您的字段在 DB 中为 NULLable,请确保相应的 .NET 类也使用 Nullable 类型。否则,各种奇怪的事情都会发生。症状通常是 NHibernate 将尝试更新 DB 中的记录,即使自从您从数据库中读取实体以来您没有更改任何字段。
The following sequence explains why this happens:
以下序列解释了为什么会发生这种情况:
- NHibernate retrieves raw entity's data from DB using ADO.NET
- NHibernate constructs the entity and sets its properties
- If DB field contained NULL the property will be set to the defaul value for its type:
- properties of reference types will be set to null
- properties of integer and floating point types will be set to 0
- properties of boolean type will be set to false
- properties of DateTime type will be set to DateTime.MinValue
- etc.
- Now, when transaction is committed, NHibernate compares the value of the property to the original field value it read form DB, and since the field contained NULL but the property contains a non-null value, NHibernate considers the property dirty, and forces an update of the enity.
- NHibernate 使用 ADO.NET 从数据库中检索原始实体的数据
- NHibernate 构造实体并设置其属性
- 如果 DB 字段包含 NULL,则该属性将设置为其类型的默认值:
- 引用类型的属性将设置为 null
- 整数和浮点类型的属性将设置为 0
- 布尔类型的属性将设置为 false
- DateTime 类型的属性将设置为 DateTime.MinValue
- 等等。
- 现在,当事务提交时,NHibernate 将属性的值与它从 DB 读取的原始字段值进行比较,并且由于该字段包含 NULL 但该属性包含非空值,NHibernate 认为该属性是脏的,并强制更新的实体。
Not only this hurts performance (you get extra round-trip to DB and extra update every time you retrieve the entity) but it also may cause hard to troubleshoot errors with DateTime columns. Indeed, when DateTime property is initialized to its default value it's set to 1/1/0001. When this value is saved to DB, ADO.NET's SqlClient can't convert it to a valid SqlDateTime value since the smallest possible SqlDateTime is 1/1/1753!!!
这不仅会损害性能(每次检索实体时都会获得额外的数据库往返和额外更新),而且还可能导致难以对 DateTime 列的错误进行故障排除。事实上,当 DateTime 属性初始化为其默认值时,它被设置为 1/1/0001。将此值保存到 DB 时,ADO.NET 的 SqlClient 无法将其转换为有效的 SqlDateTime 值,因为 SqlDateTime 的最小可能值是 1/1/1753!!!
The easiest fix is to make the class property use Nullable type, in this case "DateTime?". Alternatively, you could implement a custom type mapper by implementing IUserType with its Equals method properly comparing DbNull.Value with whatever default value of your value type. In our case Equals would need to return true when comparing 1/1/0001 with DbNull.Value. Implementing a full-functional IUserType is not really that hard but it does require knowledge of NHibernate trivia so prepare to do some substantial googling if you choose to go that way.
最简单的解决方法是使类属性使用 Nullable 类型,在本例中为“DateTime?”。或者,您可以通过实现 IUserType 及其 Equals 方法来实现自定义类型映射器,将 DbNull.Value 与您的值类型的任何默认值正确比较。在我们的例子中,当将 1/1/0001 与 DbNull.Value 进行比较时,Equals 需要返回 true。实现一个全功能的 IUserType 并不是那么难,但它确实需要 NHibernate 琐事的知识,所以如果你选择这样做,准备做一些大量的谷歌搜索。