流畅的 NHibernate 到内存 SQLite DB 在插入版本时抛出异常
我有一个相当大的域模型,它使用流畅的 NHibernate 持续到 MS SQL Server 2008。
最近我们遇到了并发问题,因此我实现了 NHibernate 的版本控制,着眼于使用 MSSQL 的时间戳数据类型。我的所有实体都是巧妙命名的 DomainEntity
抽象类的子类,该抽象类具有以下 IAutoMappingOverride
:
public class DomainEntityOverride : IAutoMappingOverride<DomainEntity> {
public void Override(AutoMapping<DomainEntity> mapping) {
mapping.OptimisticLock.Version();
mapping.Version(entity => entity.Version);
}
}
此外,我使用 IVersionConvention
设置版本控制:
public class VersionConvention : IVersionConvention {
public void Apply(IVersionInstance instance) {
instance.Column("Version");
instance.Generated.Always();
instance.UnsavedValue("null");
instance.Not.Nullable();
instance.CustomSqlType("timestamp");
}
}
我的理解是这应该导致版本始终被生成,并检查并发问题。在我的开发和生产环境(均使用 MSSQL2008)中,它似乎可以工作,但我使用 SQLite 的内存测试不再工作。
现在失败的基本测试如下:
[Test(Description = "Can save an aircraft and it is persisted")]
public void PersistAircraft_NewAircraft_AircraftIsPersisted() {
var id = saveEntity(_aircraft); //exception thrown here
var persisted = getEntity<Aircraft>(id);
Assert.That(persisted.Registration, Is.EqualTo(_aircraftRegistration));
Assert.That(persisted.Remarks, Is.EqualTo(_aircraftRemark));
}
我得到以下异常:
Test 'NOIS.Persistence.Tests.InMemory.AircraftPersistenceTest.PersistAircraft_NewAircraft_AircraftIsPersisted' failed: NHibernate.Exceptions.GenericADOException : could not insert: [NOIS.Model.Entities.AircraftType#9c2809ea-c0c5-4778-838b-9e2500a6d2ef][SQL: INSERT INTO "AircraftType" (ProducedBy, Active, Remarks, Name, Code, Category_id, Id) VALUES (?, ?, ?, ?, ?, ?, ?)]
----> System.Data.SQLite.SQLiteException : Abort due to constraint violation
AircraftType.Version may not be NULL
在 NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor会议) 在NHibernate.Persister.Entity.AbstractEntityPersister.Insert(对象id,对象[]字段,对象obj,ISessionImplementor会话) 在 NHibernate.Action.EntityInsertAction.Execute() 在NHibernate.Engine.ActionQueue.Execute(IExecutable可执行文件) 在NHibernate.Engine.ActionQueue.ExecuteActions(IList列表) 在 NHibernate.Engine.ActionQueue.ExecuteActions() 在NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource会话) 在NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent事件) 在 NHibernate.Impl.SessionImpl.Flush() 在 NHibernate.Transaction.AdoTransaction.Commit() FluentlyConfiguredInMemoryDatabaseTest.cs(49,0):位于 NOIS.Persistence.Tests.FluentlyConfiguredInMemoryDatabaseTest.saveEntity(DomainEntity 实体) InMemory\AircraftPersistenceTest.cs(33,0):位于 NOIS.Persistence.Tests.InMemory.AircraftPersistenceTest.PersistAircraft_NewAircraft_AircraftIsPersisted() --SQLite异常 在 System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt) 在 System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt) 在 System.Data.SQLite.SQLiteDataReader.NextResult() 在 System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd,CommandBehavior 行为) 在 System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior 行为) 在 System.Data.SQLite.SQLiteCommand.ExecuteNonQuery() 在NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd) 在 NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation 期望) 在NHibernate.Persister.Entity.AbstractEntityPersister.Insert(对象id,对象[]字段,布尔[] notNull,Int32 j,SqlCommandInfo sql,对象obj,ISessionImplementor会话)
I've a rather large domain-model that persists using fluent NHibernate to MS SQL Server 2008.
Recently we've been running into concurrency problems, so I implemented Versioning NHibernate's excellent Version with an eye to using MSSQL's timestamp
datatype. All my entities subclass the cunningly named DomainEntity
abstract class, which has the following IAutoMappingOverride
:
public class DomainEntityOverride : IAutoMappingOverride<DomainEntity> {
public void Override(AutoMapping<DomainEntity> mapping) {
mapping.OptimisticLock.Version();
mapping.Version(entity => entity.Version);
}
}
Furthermore I set up the versioning using an IVersionConvention
:
public class VersionConvention : IVersionConvention {
public void Apply(IVersionInstance instance) {
instance.Column("Version");
instance.Generated.Always();
instance.UnsavedValue("null");
instance.Not.Nullable();
instance.CustomSqlType("timestamp");
}
}
My understanding is that this should cause the Version to always be generated, and checked for concurrency issues. In my dev and production environments (both using MSSQL2008) it seems to work, but my in-memory tests using SQLite no longer work.
A basic test that now fails is the following:
[Test(Description = "Can save an aircraft and it is persisted")]
public void PersistAircraft_NewAircraft_AircraftIsPersisted() {
var id = saveEntity(_aircraft); //exception thrown here
var persisted = getEntity<Aircraft>(id);
Assert.That(persisted.Registration, Is.EqualTo(_aircraftRegistration));
Assert.That(persisted.Remarks, Is.EqualTo(_aircraftRemark));
}
I get the follwing exception:
Test 'NOIS.Persistence.Tests.InMemory.AircraftPersistenceTest.PersistAircraft_NewAircraft_AircraftIsPersisted' failed: NHibernate.Exceptions.GenericADOException : could not insert: [NOIS.Model.Entities.AircraftType#9c2809ea-c0c5-4778-838b-9e2500a6d2ef][SQL: INSERT INTO "AircraftType" (ProducedBy, Active, Remarks, Name, Code, Category_id, Id) VALUES (?, ?, ?, ?, ?, ?, ?)]
----> System.Data.SQLite.SQLiteException : Abort due to constraint violation
AircraftType.Version may not be NULL
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
at NHibernate.Action.EntityInsertAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()
FluentlyConfiguredInMemoryDatabaseTest.cs(49,0): at NOIS.Persistence.Tests.FluentlyConfiguredInMemoryDatabaseTest.saveEntity(DomainEntity entity)
InMemory\AircraftPersistenceTest.cs(33,0): at NOIS.Persistence.Tests.InMemory.AircraftPersistenceTest.PersistAircraft_NewAircraft_AircraftIsPersisted()
--SQLiteException
at System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt)
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)
at NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我不确定我是否完全理解你在这里所做的事情。
但据我了解,您正在使用 SQL Server 功能,即时间戳,这在 Sqlite 中不可用。所以它在那里不起作用。
据我所知,时间戳是由Sql Server在插入或更新记录时设置的。他们不受 NH 的控制。但 NH 支持它们,但需要在插入或更新后从数据库取回其值。您需要一个充分的理由在 NHibernate 中使用时间戳。
你不需要这个 SqlServer 的东西来让 NH 的乐观锁定机制工作。它实际上完全由 NH 管理,不需要数据库中的任何特殊内容。只需定义一个整数列和属性并将其映射为实体版本。其他一切都由 NH 完成。我从来不需要使用其他任何东西。
I'm not sure if I fully understand what you are doing here.
But as I understand, you are using an SQL Server feature, this timestamps, which are not available in Sqlite. So it doesn't work there.
As far as I know, timestamps are set by Sql Server when inserting or updating records. They are not under control of NH. But NH supports them, with the trade off that it needs to get its value back from the database after inserting or updating. You need a good reason to use timestamps with NHibernate.
You don't need this SqlServer stuff to get NH's optimistic locking mechanism to work. It is actually fully managed by NH and doesn't require anything special from the database. Just define a integer column and property and map it as the entities version. Everything else is done be NH. I never had to use anything else.