C#-NHibernate:逐出实体然后获取它

时间:2018-09-28 13:37:10

标签: c# .net nhibernate .net-core

我试图了解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));
            }
        }
    }
}

2 个答案:

答案 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()时:

  1. UoW(如果是NHibernate,则为ISession)指出该实体应该在某个时刻后插入
  2. 它将实体添加到会话缓存中。

关于上述第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;
    }
}