我试图了解NHibernate的工作原理。为此,我创建了一个小测试,如下所示。但是测试在标记的行上失败了,我不明白为什么。
我误会什么?
为了简要说明代码块,...在数据库中创建一个实体。然后,我调用Evict从会话缓存中删除该实体,以便对其的下一次调用将强制DB读取。然后,我进行数据库读取,但没有从数据库中读取实体实例,而是在标记行上得到了NULL。
using NHibernate;
using MyCorp.MyProject.Resources.MyEntity;
using MyCorp.MyProjectTests.Common.Fixture;
using Xunit;
namespace MyCorp.MyProjectTests.Common.DB
{
[Collection("Component")]
public class NHibernateTest
{
private readonly ISessionFactory dbSessionFactory;
public NHibernateTest(ComponentFixture componentFixture)
{
this.dbSessionFactory = componentFixture.DatabaseFixture.DBSessionFactory;
}
[Fact]
[Trait("Category", "Component")]
public void TestSessionCache()
{
const string QUERY = @"DELETE MyEntityModel mg WHERE mg.Id = :id";
const string TITLE = "NHibernate session test object";
using (ISession dbSession = this.dbSessionFactory.OpenSession())
{
// Create new entity and then remove it from session cache.
long id = (long) dbSession.Save(new MyEntityModel
{
Title = TITLE
});
dbSession.Evict(dbSession.Get<MyEntityModel>(id));
// Entity loaded from DB and stored into session cache.
Assert.Equal(TITLE, dbSession.Get<MyEntityModel>(id).Title); // ===== FAILS HERE =====
// Delete entity from DB, but don't evict from session cache yet.
dbSession.CreateQuery(QUERY).SetParameter("id", id).ExecuteUpdate();
// Entity still reachable through session cache.
Assert.Equal(TITLE, dbSession.Get<MyEntityModel>(id).Title);
// Evict deleted entity from session cache.
dbSession.Evict(dbSession.Get<MyEntityModel>(id));
// Entity not available in neither DB nor session cache.
Assert.Null(dbSession.Get<MyEntityModel>(id));
}
}
}
}
答案 0 :(得分:1)
Save()不等于SQL INSERT。
Save()的意思是:使会话知道该对象,并让该会话在适当的时候将其发送到数据库 。根据映射和配置,此可以在Save()返回之前,也可以不在。
因此,您可以在会话保留之前将其从会话中逐出。
如果省略对Evict()的调用,则测试有效,因为其他任何代码实际上都不依赖于数据库中的项目(DELETE语句可能表明它发现有0行要删除,但这不是问题。进行测试)。
要使用自动刷新行为,您应该始终处于事务内,而不仅仅是会话。实际上,为了获得最高的可靠性,实际上,无论何时在会话中,您都应该始终处于事务内(其他模式是可行的,但是正确起来往往会更复杂)。
以下是何时清除数据库更改的文档: http://nhibernate.info/doc/nhibernate-reference/manipulatingdata.html#manipulatingdata-flushing
答案 1 :(得分:0)
@OskarBerggren的答案解释了Save()
方法的工作。我是用UoW来回答的。
借助NHibernate之类的ORM,您可以更好地了解Unit Of Work,然后再使用它。正是这里的问题。
UoW在给定的UoW范围内跟踪“做什么”动作。因此,当您致电Save()
时:
ISession
)指出该实体应该在某个时刻后插入。关于上述第1点,“在某个点后”由FlushMode
定义。根据冲洗模式设置,它可能不会立即插入。
要进行测试,您可以将FlushMode
设置为MANUAL
,然后在Flush()
之后调用Save()
。或者,您可以将其设置为ALWAYS
。在实施此之前,请研究链接的文档。冲洗模式应谨慎选择。
您的代码不适用于UoW。我通常将刷新模式设置为COMMIT
,以便将NHibernate的UoW最佳利用。范围结束时会刷新UoW(所有更改都将保留)。
public sealed class DbSession : IDbSession
{
public DbSession()
{
session = sessionFactory.OpenSession();
transaction = session.BeginTransaction();
}
ISession session = null;
ITransaction transaction = null;
/*
* By default, the instance should be dirty.
* Caller should explicitly set the property to 'false' in calling code if everything was fine.
* If caller fail to set this property, it will be assumed that caller do not want to flush
* the changes to database.
*/
bool isDirty = true;
bool IDbSession.IsDirty { set { isDirty = value; } }
public void Dispose()
{
if(session == null)
return;
if(isDirty == false)
transaction.Commit();
else
transaction.Rollback();
transaction.Dispose();
transaction = null;
session.Dispose();
session = null;
}
}