您好我正在使用asp.net Web应用程序的每请求事务(session-in-view)模式。我在应用程序中有几个要点,我想保存一个NHibernate管理实体,然后使用常见的SQL执行几个插入和更新。这些插入/更新取决于NH保存实体将采用的ID。
问题是生成的id在事务范围内不存在。如果我强制刷新/提交id是持久的,但如果插入/更新失败,我必须回滚,但刷新/提交的实体不会。目前我正在为这些案例进行手动插入,但这是我想要改变的。那么,有没有办法在Save()之后执行SQL语句(在已经打开的事务中)但不强制刷新/提交?
编辑:我正在添加一个半伪代码示例,我得到了4个错误的答案,所以我认为人们不理解(NHibernate如何工作) 在Begin请求我发出nhsession.BeginTransaction()
然后在某些时候我做
FooClass fc = new FooClass("value");
nhsession.Save(fc);
ITransaction trans = nhsession.Transaction;
SqlCommand sc = new SqlCommand("some insert/update query that depends on fc's id", (SqlConnection)nhsession.Connection);
sc.Parameters.Add("id", fc.Id); //NHibernate generates the id, note i'm using assigned/hi-lo so no round trip to the db takes place
transaction.Enlist(sc);
try {
sc.ExecuteNonQuery();
}
catch (SqlException ex){
transaction.RollBack();
nhsession.Close();
}
并在请求结束时发出CommitTransaction()
和nhsession.Close()
现在这绝对不会做任何事情:FooClass (fc)
尚未刷新/提交到数据库。 NH已完成的Save()
操作直到内存中的那一点。这意味着nhibernate没有发出sql命令,这意味着我之后触发的SqlCommand (sc)
将失败,因为id不存在。
如果我在Save()
和SqlCommand
FooClass(fc)
_cannot_be_rolled_back_之间进行刷新/提交,这是一件坏事。
目前,为了工作,我使用SqlCommand
制作vanila sql插件,我想改变它。 (为什么?因为我不想制作香草插页,因为模式/模型更改而容易出错,我得到了OR / M)
如何?我想以某种方式通知NHibernate执行SqlCommand
以对应Save()
插入(地狱,它可以执行它收集的所有SqlCommands)但没有它提交或刷新!。
目前我还在搜索nhibernate在刷新/提交已保存对象时产生的准备好的sql语句。也许我可以只接受该字符串并在我在事务中登记的SqlCommand中运行它。
答案 0 :(得分:5)
也许我不明白,但你不能这样做......
using(var tx = session.BeginTransaction())
{
session.Save(entity);
session.Flush();
//perform non NH operations with entity.id
tx.Commit();
}//uncommitted transaction would be rolled back on dispose()
答案 1 :(得分:1)
您是否可以使用嵌套事务或事务范围来支持此操作?
基本上你要问的是nh在你使用它之前分配id,生成的id只在刷新更改时分配,更改只在事务边界刷新,因此你不能创建一个内部事务范围来包装nh相关持久性强制刷新和id生成但仍然允许外部事务失败并在其中一个存储过程失败时回滚所有工作。
How do I do nested transactions in NHibernate?有一些示例代码,用于演示使用范围的方法。
答案 2 :(得分:0)
在交易过程中听起来像是想要“脏读”隔离级别。这将允许您阅读'uncommited'但是'saved'的内容。然而,我对NHibernate不够流利,无法提供有关如何在那里做到的任何见解。
我确定其他人可以参与如何为NH进行'脏读'隔离级别。
答案 3 :(得分:0)
我在NHibernate之上使用Castle ActiveRecord但是原理应该是相同的,我的DTC被禁用所以我不肯定这是有效的,但这应该是正确的方向。
Imports Castle.ActiveRecord
imports NHibernate.Expression
<ActiveRecord> _
Public Class Test1
Inherits ActiveRecordBase(Of Test1)
Private _id As Guid
<PrimaryKeyAttribute(PrimaryKeyType.Guid)> _
Public overridable Property Id as Guid
Get
return _id
End Get
Set(value As guid)
_id = value
End Set
End Property
Private _prop1 As String
<[Property]> _
Public overridable Property prop1 as String
Get
return _prop1
End Get
Set(value As String)
_prop1 = value
End Set
End Property
End Class
<ActiveRecord> _
Public Class Test2
Inherits ActiveRecordBase(Of Test2)
Private _id As Guid
<PrimaryKey(PrimaryKeyType.Guid)> _
Public overridable Property Id as Guid
Get
return _id
End Get
Set(value As guid)
_id = value
End Set
End Property
Private _prop1 As String
<[Property]> _
Public overridable Property prop1 as String
Get
return _prop1
End Get
Set(value As String)
_prop1 = value
End Set
End Property
Private _t1 As Test1
<BelongsTo()> _
Public overridable Property t1 as Test1
Get
return _t1
End Get
Set(value As test1)
_t1 = value
End Set
End Property
End Class
Imports Castle.ActiveRecord
Imports NHibernate.Expression
Imports System.Data.SqlClient
Imports System.Transactions
imports System.Configuration
Public Module Module1
Public Class modMain
Public Shared Sub Main()
Castle.ActiveRecord.ActiveRecordStarter.Initialize()
Using T As New System.Transactions.TransactionScope(ondispose.Rollback)
Dim x As New Test1()
x.prop1 = "Hello"
x.Save
using c As New SqlConnection()
c.ConnectionString = ConfigurationManager.ConnectionStrings("Development").ConnectionString
Dim cmd As SqlCommand = c.CreateCommand()
cmd.CommandText = "insert into test2(prop1, t1) values(@prop1, @t1)"
Dim p As SqlParameter = cmd.CreateParameter()
p.Direction = parameterdirection.Input
p.DbType = dbtype.Guid
p.ParameterName = "@prop1"
p.Value = "Test"
cmd.Parameters.Add(p)
p = cmd.CreateParameter()
p.Direction = parameterdirection.Input
p.DbType = dbtype.Guid
p.ParameterName = "@t1"
p.Value = x.Id
c.Open
cmd.ExecuteNonQuery
end using
t.Complete
end using
End Sub
End Class
End Module
答案 4 :(得分:0)
您是否尝试过使用NHibernate的直接SQL功能?而不是突破到NHIBnate所知道的SQLCommand,你可以使用ISession.CreateSQLQuery(“native SQL here”)。如果你在Save方法之后在事务中执行此操作,NHibernate应该在Commit发生时按顺序执行所有语句。因此:
using(var tx = session.BeginTransaction())
{
try
{
FooClass fc = new FooClass("value");
nhsession.Save(fc);
var nativeQuery = nhsession.CreateSQLQuery("some insert/update query that depends on fc's id");
// Add parameters to nativeQuery using SetXXX methods
nativeQuery.SetXXX(...);
// Save nativeQuery
nativeQuery.Save(); // I think...
tx.Commit();
}
catch (...)
{
tx.Rollback();
}
}
有关使用NH原生查询的另一个类似问题,请参阅此处:Hibernate NHibernate - Native SQL