nhibernate / nhibernate-core

NHibernate Object Relational Mapper
https://nhibernate.info
GNU Lesser General Public License v2.1
2.11k stars 921 forks source link

Future/FutureValue does not load NoLazy children #3565

Open VoNhatSinh opened 1 week ago

VoNhatSinh commented 1 week ago

Please see the attached file for a reproducible project (also includes database script, and unit test) FutureDoesNotLoadNoLazyChildNHibernate.zip

Steps to reproduce:

In the context of my example project, I have a Post table and a Comment table. A Post has a one-to-many relationship with Comment. The PostMap is configured to use CollectionLazy.NoLazy for Comments.

I wrote four unit tests in the attached project to demonstrate an issue. I use Future/FutureValue to get Posts/Post, and I expected the related child Comments to be loaded and usable outside the session. However, it throws a LazyInitializationException.

I then tried another approach using QueryOver.List (which returns a list of entities) or QueryOver.SingleOrDefault, and in this case, I can use the Comments as expected.

Is this the expected behavior of Future/FutureValue or is it a bug?

public class TestLoadingNoLazy
    {
        private ISessionFactory _sessionFactory;

        [OneTimeSetUp]
        public void OneTimeSetUp()
        {
            var config = new Configuration();
            config = config.DataBaseIntegration(db =>
            {
                db.ConnectionString = @"Server=localhost;Database=NoLazyNHibernate;Integrated Security=true;";
                db.Dialect<MsSql2012Dialect>();
                db.Driver<Sql2008ClientDriver>();
            });

            var mapper = new ConventionModelMapper();
            mapper.AddMapping<PostMap>();
            mapper.AddMapping<CommentMap>();
            var hbmMappings = mapper.CompileMappingForAllExplicitlyAddedEntities();

            config.AddMapping(hbmMappings);
            _sessionFactory = config.BuildSessionFactory();
        }

        [OneTimeTearDown]
        public void OneTimeTearDown()
        {
            _sessionFactory.Close();
        }

        [Test]
        public void LoadListPosts__UsingFuture__ThrowLazyInitializationException()
        {
            // Arrange
            IList<Post> postFuture;
            using (var session = _sessionFactory.OpenSession())
            {
                postFuture = session.QueryOver<Post>()
                    .Where(x => x.Title == "Title")
                    .Future().ToList();
            }

            // Action
            Func<string> action = () => postFuture[0].Comments[0].Content;

            // Assert
            action.Should().ThrowExactly<LazyInitializationException>();
        }

        [Test]
        public void LoadListPosts__UsingList__LoadCommentsSuccessfully()
        {
            // Arrange
            IList<Post> postList;
            using (var session = _sessionFactory.OpenSession())
            {
                postList = session.QueryOver<Post>()
                    .Where(x => x.Title == "Title")
                    .List();
            }

            // Action
            Func<string> action = () => postList[0].Comments[0].Content;

            // Assert
            action.Should().NotThrow();
        }

        [Test]
        public void LoadOnePost__UsingFutureValue__ThrowLazyInitializationException()
        {
            // Arrange
            Post postFutureValue;
            using (var session = _sessionFactory.OpenSession())
            {
                postFutureValue = session.QueryOver<Post>()
                    .Where(x => x.Title == "Title")
                    .FutureValue().Value;
            }

            // Action
            Func<string> action = () => postFutureValue.Comments[0].Content;

            // Assert
            action.Should().ThrowExactly<LazyInitializationException>();
        }

        [Test]
        public void LoadOnePost__UsingSingleOrDefault__LoadCommentsSuccessfully()
        {
            // Arrange
            Post postFutureValue;
            using (var session = _sessionFactory.OpenSession())
            {
                postFutureValue = session.QueryOver<Post>()
                    .Where(x => x.Title == "Title")
                    .SingleOrDefault();
            }

            // Action
            Func<string> action = () => postFutureValue.Comments[0].Content;

            // Assert
            action.Should().NotThrow();
        }
    }

Please help me check. Thanks in advance.