diff --git a/releasenotes.txt b/releasenotes.txt index bfd2a67804c..b5766fbf4bb 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -1,4 +1,26 @@ -Build 5.3.3 +Build 5.3.4 +============================= + +Release notes - NHibernate - Version 5.3.4 + +6 issues were resolved in this release. + +** Bug + + * #2580 InvalidWithClauseException when join polymorphic entity + * #2559 Regression in caching linq query with ThenFetchMany statement. + * #2549 ApplyFilter does not work on join statements in LINQ + * #2537 Unable to cast "System.Linq.Expressions.UnaryExpression" to "System.Linq.Expressions.LambdaExpression". + +** Task + + * #2578 Add missing possible breaking changes for #2365 + * #2587 Release 5.3.4 + +As part of releasing 5.3.4, one missing 5.3.0 possible breaking change has been added, about +custom method generators for Linq. See 5.3.0 possible breaking changes. + +Build 5.3.3 ============================= Release notes - NHibernate - Version 5.3.3 @@ -135,6 +157,8 @@ Release notes - NHibernate - Version 5.3.0 already exists in the set will now return false. * Calling `ISet<>.Add` or `ICollection<>.Add` on an uninitialized set mapped as `lazy="true"` with a transient element that does not override `Equals` method will not initialize the collection. + * Linq custom generators deriving from `BaseHqlGeneratorForMethod` should override the + `TryGetCollectionParameter` method if they have to support parameter lists. ** Bug diff --git a/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs b/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs index 987ed002f60..9fda2e873c9 100644 --- a/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs +++ b/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs @@ -148,5 +148,13 @@ public async Task CanJoinOnEntityWithSubclassesAsync() from o2 in db.Animals.Where(x => x.BodyWeight > 50) select new {o, o2}).Take(1).ToListAsync()); } + + [Test(Description = "GH-2580")] + public async Task CanInnerJoinOnSubclassWithBaseTableReferenceInOnClauseAsync() + { + var result = await ((from o in db.Animals + join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight + select new { o, o2 }).Take(1).ToListAsync()); + } } } diff --git a/src/NHibernate.Test/Async/Linq/WhereSubqueryTests.cs b/src/NHibernate.Test/Async/Linq/WhereSubqueryTests.cs index a4454eab881..e21d2ca8969 100644 --- a/src/NHibernate.Test/Async/Linq/WhereSubqueryTests.cs +++ b/src/NHibernate.Test/Async/Linq/WhereSubqueryTests.cs @@ -788,5 +788,30 @@ public async Task NullComparedToMemberInitExpressionInWhereClauseAsync() Assert.That(result.Count, Is.EqualTo(45)); } + + public class Specification + { + private Expression> _expression; + + public Specification(Expression> expression) + { + _expression = expression; + } + + public static implicit operator Expression>(Specification specification) + { + return specification._expression; + } + } + + [Test] + public async Task ImplicitConversionInsideWhereSubqueryExpressionAsync() + { + if (!Dialect.SupportsScalarSubSelects) + Assert.Ignore(Dialect.GetType().Name + " does not support scalar sub-queries"); + + var spec = new Specification(x => x.Freight > 1000); + await (db.Orders.Where(o => db.Orders.Where(spec).Any(x => x.OrderId == o.OrderId)).ToListAsync()); + } } } diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2549/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2549/Fixture.cs new file mode 100644 index 00000000000..82227867203 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2549/Fixture.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Linq; +using NUnit.Framework; +using NHibernate.Linq; + +namespace NHibernate.Test.NHSpecificTest.GH2549 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void OnSetUp() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(new Person {Id = 1, Name = "Name"}); + s.Save(new Customer {Deleted = false, Name = "Name", Id = 1}); + s.Save(new Customer {Deleted = true, Name = "Name", Id = 2}); + + t.Commit(); + } + } + + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from System.Object").ExecuteUpdate(); + t.Commit(); + } + } + + [Test] + public async Task EntityJoinFilterLinqAsync() + { + using (var s = OpenSession()) + { + var list = await ((from p in s.Query() + join c in s.Query() on p.Name equals c.Name + select p).ToListAsync()); + + s.EnableFilter("DeletedCustomer").SetParameter("deleted", false); + + var filteredList = await ((from p in s.Query() + join c in s.Query() on p.Name equals c.Name + select p).ToListAsync()); + + Assert.That(list, Has.Count.EqualTo(2)); + Assert.That(filteredList, Has.Count.EqualTo(1)); + } + } + + [Test] + public async Task EntityJoinFilterQueryOverAsync() + { + using (var s = OpenSession()) + { + Customer c = null; + Person p = null; + var list = await (s.QueryOver(() => p).JoinEntityAlias(() => c, () => c.Name == p.Name).ListAsync()); + + s.EnableFilter("DeletedCustomer").SetParameter("deleted", false); + + var filteredList = await (s.QueryOver(() => p).JoinEntityAlias(() => c, () => c.Name == p.Name).ListAsync()); + + Assert.That(list, Has.Count.EqualTo(2)); + Assert.That(filteredList, Has.Count.EqualTo(1)); + } + } + } +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2559/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2559/FixtureByCode.cs new file mode 100644 index 00000000000..8dea23c6d76 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2559/FixtureByCode.cs @@ -0,0 +1,156 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Linq; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2559 +{ + using System.Threading.Tasks; + [TestFixture] + public class ByCodeFixtureAsync : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class(rc => + { + rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); + rc.Property(x => x.Name); + rc.Property(x => x.Age); + rc.Set( + x => x.Children, + colMap => + { + colMap.Inverse(true); + colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans); + colMap.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, + rel => rel.OneToMany()); + rc.Set( + x => x.Cars, + colMap => + { + colMap.Inverse(true); + colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans); + colMap.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, + rel => rel.OneToMany()); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + mapper.Class(ch => + { + ch.Id(x => x.Id, m => m.Generator(Generators.Guid)); + ch.Property(x => x.Name); + ch.ManyToOne(c => c.Parent); + + ch.Set( + x => x.Pets, + colMap => + { + colMap.Inverse(true); + colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans); + colMap.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, + rel => rel.OneToMany()); + + ch.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + mapper.Class(ch => + { + ch.Id(x => x.Id, m => m.Generator(Generators.Guid)); + ch.Property(x => x.Name); + ch.ManyToOne(c => c.Owner); + + ch.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + mapper.Class(ch => + { + ch.Id(x => x.Id, m => m.Generator(Generators.Guid)); + ch.Property(x => x.Name); + ch.ManyToOne(c => c.Owner); + + ch.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var person = new Person { Name = "Person 1", Age = 18 }; + + var car1 = new Car { Name = "Car1", Owner = person }; + var car2 = new Car { Name = "Car2", Owner = person }; + session.Save(car1); + session.Save(car2); + + session.Save(person); + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from Pet").ExecuteUpdate(); + session.CreateQuery("delete from Child").ExecuteUpdate(); + session.CreateQuery("delete from Car").ExecuteUpdate(); + session.CreateQuery("delete from Person").ExecuteUpdate(); + transaction.Commit(); + } + } + + [Test] + public async Task TestQueryCachingWithThenFetchManyAsync() + { + Person dbPerson; + Person cachePerson; + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var query = + session + .Query() + .FetchMany(p => p.Children) + .ThenFetchMany(ch => ch.Pets) + .FetchMany(p => p.Cars) as IQueryable; + + query = query.WithOptions(opt => + opt.SetCacheable(true) + .SetCacheMode(CacheMode.Normal) + .SetCacheRegion("Long_Cache")); + + dbPerson = (await (query.ToListAsync())).First(); // First time the result will be cached + cachePerson = (await (query.ToListAsync())).First(); + + await (transaction.CommitAsync()); + } + + Assert.That(NHibernateUtil.IsInitialized(dbPerson.Cars), Is.True); + Assert.That(NHibernateUtil.IsInitialized(cachePerson.Cars), Is.True); + Assert.That(dbPerson.Cars, Has.Count.EqualTo(2)); + Assert.That(cachePerson.Cars, Has.Count.EqualTo(2)); + + Assert.That(NHibernateUtil.IsInitialized(dbPerson.Children), Is.True); + Assert.That(NHibernateUtil.IsInitialized(cachePerson.Children), Is.True); + Assert.That(dbPerson.Children, Has.Count.EqualTo(0)); + Assert.That(cachePerson.Children, Has.Count.EqualTo(0)); + } + } +} diff --git a/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs b/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs index 8dce4d39223..a7a4750b87a 100644 --- a/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs +++ b/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs @@ -137,5 +137,13 @@ public void CanJoinOnEntityWithSubclasses() from o2 in db.Animals.Where(x => x.BodyWeight > 50) select new {o, o2}).Take(1).ToList(); } + + [Test(Description = "GH-2580")] + public void CanInnerJoinOnSubclassWithBaseTableReferenceInOnClause() + { + var result = (from o in db.Animals + join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight + select new { o, o2 }).Take(1).ToList(); + } } } diff --git a/src/NHibernate.Test/Linq/WhereSubqueryTests.cs b/src/NHibernate.Test/Linq/WhereSubqueryTests.cs index 6ee1f9ce667..543a5380461 100644 --- a/src/NHibernate.Test/Linq/WhereSubqueryTests.cs +++ b/src/NHibernate.Test/Linq/WhereSubqueryTests.cs @@ -956,5 +956,30 @@ public void NullComparedToMemberInitExpressionInWhereClause() Assert.That(result.Count, Is.EqualTo(45)); } + + public class Specification + { + private Expression> _expression; + + public Specification(Expression> expression) + { + _expression = expression; + } + + public static implicit operator Expression>(Specification specification) + { + return specification._expression; + } + } + + [Test] + public void ImplicitConversionInsideWhereSubqueryExpression() + { + if (!Dialect.SupportsScalarSubSelects) + Assert.Ignore(Dialect.GetType().Name + " does not support scalar sub-queries"); + + var spec = new Specification(x => x.Freight > 1000); + db.Orders.Where(o => db.Orders.Where(spec).Any(x => x.OrderId == o.OrderId)).ToList(); + } } } diff --git a/src/NHibernate.Test/NHSpecificTest/GH2549/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2549/Fixture.cs new file mode 100644 index 00000000000..a01c1ec2572 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2549/Fixture.cs @@ -0,0 +1,70 @@ +using System.Linq; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2549 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnSetUp() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Save(new Person {Id = 1, Name = "Name"}); + s.Save(new Customer {Deleted = false, Name = "Name", Id = 1}); + s.Save(new Customer {Deleted = true, Name = "Name", Id = 2}); + + t.Commit(); + } + } + + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete from System.Object").ExecuteUpdate(); + t.Commit(); + } + } + + [Test] + public void EntityJoinFilterLinq() + { + using (var s = OpenSession()) + { + var list = (from p in s.Query() + join c in s.Query() on p.Name equals c.Name + select p).ToList(); + + s.EnableFilter("DeletedCustomer").SetParameter("deleted", false); + + var filteredList = (from p in s.Query() + join c in s.Query() on p.Name equals c.Name + select p).ToList(); + + Assert.That(list, Has.Count.EqualTo(2)); + Assert.That(filteredList, Has.Count.EqualTo(1)); + } + } + + [Test] + public void EntityJoinFilterQueryOver() + { + using (var s = OpenSession()) + { + Customer c = null; + Person p = null; + var list = s.QueryOver(() => p).JoinEntityAlias(() => c, () => c.Name == p.Name).List(); + + s.EnableFilter("DeletedCustomer").SetParameter("deleted", false); + + var filteredList = s.QueryOver(() => p).JoinEntityAlias(() => c, () => c.Name == p.Name).List(); + + Assert.That(list, Has.Count.EqualTo(2)); + Assert.That(filteredList, Has.Count.EqualTo(1)); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2549/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH2549/Mappings.hbm.xml new file mode 100644 index 00000000000..65b540b9202 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2549/Mappings.hbm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/GH2549/Model.cs b/src/NHibernate.Test/NHSpecificTest/GH2549/Model.cs new file mode 100644 index 00000000000..b6d678c7b20 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2549/Model.cs @@ -0,0 +1,15 @@ +namespace NHibernate.Test.NHSpecificTest.GH2549 +{ + public class Customer + { + public virtual int Id { get; set; } + public virtual bool Deleted { get; set; } + public virtual string Name { get; set; } + } + + public class Person + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Car.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Car.cs new file mode 100644 index 00000000000..b93e028c8b6 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Car.cs @@ -0,0 +1,12 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH2559 +{ + public class Car + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + + public virtual Person Owner { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Child.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Child.cs new file mode 100644 index 00000000000..43f56d8b701 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Child.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.GH2559 +{ + public class Child + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + + public virtual Person Parent { get; set; } + + public virtual ISet Pets + { + get => _pets ?? (_pets = new HashSet()); + set => _pets = value; + } + private ISet _pets; + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/FixtureByCode.cs new file mode 100644 index 00000000000..33348db8bd9 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2559/FixtureByCode.cs @@ -0,0 +1,145 @@ +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Linq; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2559 +{ + [TestFixture] + public class ByCodeFixture : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class(rc => + { + rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); + rc.Property(x => x.Name); + rc.Property(x => x.Age); + rc.Set( + x => x.Children, + colMap => + { + colMap.Inverse(true); + colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans); + colMap.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, + rel => rel.OneToMany()); + rc.Set( + x => x.Cars, + colMap => + { + colMap.Inverse(true); + colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans); + colMap.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, + rel => rel.OneToMany()); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + mapper.Class(ch => + { + ch.Id(x => x.Id, m => m.Generator(Generators.Guid)); + ch.Property(x => x.Name); + ch.ManyToOne(c => c.Parent); + + ch.Set( + x => x.Pets, + colMap => + { + colMap.Inverse(true); + colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans); + colMap.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, + rel => rel.OneToMany()); + + ch.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + mapper.Class(ch => + { + ch.Id(x => x.Id, m => m.Generator(Generators.Guid)); + ch.Property(x => x.Name); + ch.ManyToOne(c => c.Owner); + + ch.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + mapper.Class(ch => + { + ch.Id(x => x.Id, m => m.Generator(Generators.Guid)); + ch.Property(x => x.Name); + ch.ManyToOne(c => c.Owner); + + ch.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var person = new Person { Name = "Person 1", Age = 18 }; + + var car1 = new Car { Name = "Car1", Owner = person }; + var car2 = new Car { Name = "Car2", Owner = person }; + session.Save(car1); + session.Save(car2); + + session.Save(person); + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from Pet").ExecuteUpdate(); + session.CreateQuery("delete from Child").ExecuteUpdate(); + session.CreateQuery("delete from Car").ExecuteUpdate(); + session.CreateQuery("delete from Person").ExecuteUpdate(); + transaction.Commit(); + } + } + + [Test] + public void TestQueryCachingWithThenFetchMany() + { + Person dbPerson; + Person cachePerson; + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var query = + session + .Query() + .FetchMany(p => p.Children) + .ThenFetchMany(ch => ch.Pets) + .FetchMany(p => p.Cars) as IQueryable; + + query = query.WithOptions(opt => + opt.SetCacheable(true) + .SetCacheMode(CacheMode.Normal) + .SetCacheRegion("Long_Cache")); + + dbPerson = query.ToList().First(); // First time the result will be cached + cachePerson = query.ToList().First(); + + transaction.Commit(); + } + + Assert.That(NHibernateUtil.IsInitialized(dbPerson.Cars), Is.True); + Assert.That(NHibernateUtil.IsInitialized(cachePerson.Cars), Is.True); + Assert.That(dbPerson.Cars, Has.Count.EqualTo(2)); + Assert.That(cachePerson.Cars, Has.Count.EqualTo(2)); + + Assert.That(NHibernateUtil.IsInitialized(dbPerson.Children), Is.True); + Assert.That(NHibernateUtil.IsInitialized(cachePerson.Children), Is.True); + Assert.That(dbPerson.Children, Has.Count.EqualTo(0)); + Assert.That(cachePerson.Children, Has.Count.EqualTo(0)); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Person.cs new file mode 100644 index 00000000000..8d125021ec9 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Person.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.GH2559 +{ + public class Person + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + public virtual int Age { get; set; } + + public virtual ISet Cars + { + get => _cars ?? (_cars = new HashSet()); + set => _cars = value; + } + private ISet _cars; + + public virtual ISet Children + { + get => _children ?? (_children = new HashSet()); + set => _children = value; + } + private ISet _children; + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Pet.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Pet.cs new file mode 100644 index 00000000000..7b580d24bd1 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Pet.cs @@ -0,0 +1,12 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH2559 +{ + public class Pet + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + + public virtual Child Owner { get; set; } + } +} diff --git a/src/NHibernate/Async/Type/CollectionType.cs b/src/NHibernate/Async/Type/CollectionType.cs index b103b445c22..432c11957a8 100644 --- a/src/NHibernate/Async/Type/CollectionType.cs +++ b/src/NHibernate/Async/Type/CollectionType.cs @@ -104,6 +104,11 @@ public override async Task DisassembleAsync(object value, ISessionImplem public override async Task BeforeAssembleAsync(object oid, ISessionImplementor session, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + if (oid == null) + { + return; + } + var queryCacheQueue = session.PersistenceContext.BatchFetchQueue.QueryCacheQueue; if (queryCacheQueue == null) { diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs index acb6b2b81db..4458d5cecce 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs @@ -1,4 +1,6 @@ -using Antlr.Runtime; +using System; +using Antlr.Runtime; +using NHibernate.Engine; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Type; @@ -15,11 +17,9 @@ public EntityJoinFromElement(FromClause fromClause, IQueryable entityPersister, EntityType entityType = (EntityType) entityPersister.Type; InitializeEntity(fromClause, entityPersister.EntityName, entityPersister, entityType, alias, tableAlias); - JoinSequence = new EntityJoinJoinSequenceImpl( - SessionFactoryHelper.Factory, - entityType, - tableAlias, - joinType); + //NH Specific: hibernate uses special class EntityJoinJoinSequenceImpl + JoinSequence = new JoinSequence(SessionFactoryHelper.Factory) + .AddJoin(entityType, tableAlias, joinType, Array.Empty()); fromClause.Walker.AddQuerySpaces(entityPersister.QuerySpaces); } diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinJoinSequenceImpl.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinJoinSequenceImpl.cs deleted file mode 100644 index 1c41d857cd0..00000000000 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinJoinSequenceImpl.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using NHibernate.Engine; -using NHibernate.SqlCommand; -using NHibernate.Type; - -namespace NHibernate.Hql.Ast.ANTLR.Tree -{ - class EntityJoinJoinSequenceImpl : JoinSequence - { - public EntityJoinJoinSequenceImpl(ISessionFactoryImplementor factory, EntityType entityType, string tableAlias, JoinType joinType):base(factory) - { - AddJoin(entityType, tableAlias, joinType, Array.Empty()); - //Note: filters don't work with entity joins - //as EntytyType.GetOnCondition always returns empty string for entity join (as IsReferenceToPrimaryKey is always true). - } - } -} diff --git a/src/NHibernate/Linq/Visitors/NhPartialEvaluatingExpressionVisitor.cs b/src/NHibernate/Linq/Visitors/NhPartialEvaluatingExpressionVisitor.cs index 475dbcdad31..c92403cbc82 100644 --- a/src/NHibernate/Linq/Visitors/NhPartialEvaluatingExpressionVisitor.cs +++ b/src/NHibernate/Linq/Visitors/NhPartialEvaluatingExpressionVisitor.cs @@ -157,7 +157,9 @@ private Expression EvaluateSubtree(Expression subtree) private bool ContainsVariable(Expression expression) { - if (!(expression is UnaryExpression unaryExpression)) + if (!(expression is UnaryExpression unaryExpression) || + // Avoid detecting expression variables as parameters + typeof(Expression).IsAssignableFrom(expression.Type)) { return false; } diff --git a/src/NHibernate/Type/CollectionType.cs b/src/NHibernate/Type/CollectionType.cs index 4ac7c434d53..408e03d94ba 100644 --- a/src/NHibernate/Type/CollectionType.cs +++ b/src/NHibernate/Type/CollectionType.cs @@ -160,6 +160,11 @@ public override object Disassemble(object value, ISessionImplementor session, ob public override void BeforeAssemble(object oid, ISessionImplementor session) { + if (oid == null) + { + return; + } + var queryCacheQueue = session.PersistenceContext.BatchFetchQueue.QueryCacheQueue; if (queryCacheQueue == null) {