diff --git a/src/NHibernate.Test/NHSpecificTest/NH3596/CachingWithTransformerTests.cs b/src/NHibernate.Test/NHSpecificTest/NH3596/CachingWithTransformerTests.cs new file mode 100644 index 00000000000..35f3feb2788 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3596/CachingWithTransformerTests.cs @@ -0,0 +1,175 @@ +using System.Collections.Generic; +using System.Linq; +using NHibernate.Cache; +using NHibernate.Cfg; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NHibernate.Mapping.ByCode.Conformist; +using NHibernate.Transform; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3596 +{ + public class EntityBase + { + public virtual int Id { get; set; } + } + + public class Role : EntityBase + { + public virtual string Name { get; set; } + public virtual Role Parent { get; set; } + public virtual IList Children { get; set; } + } + + public class RoleMap : ClassMapping + { + public RoleMap() + { + Cache(x => x.Usage(CacheUsage.ReadOnly)); + Id(x => x.Id, x => x.Generator(Generators.Identity)); + Property(x => x.Name); + ManyToOne(x => x.Parent, x => + { + x.Column("ParentId"); + x.NotNullable(false); + }); + Bag(x => x.Children, x => + { + x.Cascade(Mapping.ByCode.Cascade.All); + x.Key(y => y.Column("ParentId")); + }, x => x.OneToMany()); + } + } + + public class CachingWithTransformerTests: TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.AddMapping(); + var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities(); + return mapping; + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var parentRoles = new List() + { + new Role() { Name = "Admin", Children = null, Parent = null }, + new Role() { Name = "Manager", Children = null, Parent = null }, + new Role() { Name = "Support", Children = null, Parent = null } + }; + + var childRoles = new List() + { + new Role() { Name = "Manager-Secretary", Children = null, Parent = parentRoles.FirstOrDefault(x => x.Name == "Manager") }, + new Role() { Name = "Superviser", Children = null, Parent = parentRoles.FirstOrDefault(x => x.Name == "Manager") } + }; + + foreach (var parentRole in parentRoles) + { + parentRole.Children = new List(childRoles.Where(x => x.Parent == parentRole)); + + session.Save(parentRole); + } + + session.Flush(); + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } + + protected override void Configure(Cfg.Configuration configuration) + { + base.Configure(configuration); + + configuration.Cache(x => + { + x.Provider(); + x.UseQueryCache = true; + }); + } + + [Test] + public void UsingQueryOverToFutureWithCacheAndTransformerDoesntThrow() + { + //NH-3596 + using (var session = this.OpenSession()) + using (session.BeginTransaction()) + { + Role children = null; + Role parent = null; + + //store values in cache + session + .QueryOver() + .Where(x => x.Parent == null) + .Left.JoinAlias(x => x.Children, () => children) + .Left.JoinAlias(x => x.Parent, () => parent) + .TransformUsing(Transformers.DistinctRootEntity) + .Cacheable() + .CacheMode(CacheMode.Normal) + .Future(); + + //get values from cache + var roles = session + .QueryOver() + .Where(x => x.Parent == null) + .Left.JoinAlias(x => x.Children, () => children) + .Left.JoinAlias(x => x.Parent, () => parent) + .TransformUsing(Transformers.DistinctRootEntity) + .Cacheable() + .CacheMode(CacheMode.Normal) + .Future(); + + var result = roles.ToList(); + + Assert.AreEqual(3, result.Count); + } + } + + [Test] + public void UsingHqlToFutureWithCacheAndTransformerDoesntThrow() + { + //NH-3596 + using (var session = this.OpenSession()) + using (session.BeginTransaction()) + { + //store values in cache + session + .CreateQuery("select r from Role r left join r.Children left join r.Parent where r.Parent is null") + .SetResultTransformer(Transformers.DistinctRootEntity) + .SetCacheable(true) + .SetCacheMode(CacheMode.Normal) + .Future(); + + //get values from cache + var roles = session + .CreateQuery("select r from Role r left join r.Children left join r.Parent where r.Parent is null") + .SetResultTransformer(Transformers.DistinctRootEntity) + .SetCacheable(true) + .SetCacheMode(CacheMode.Normal) + .Future(); + + var result = roles.ToList(); + + Assert.AreEqual(3, result.Count); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index 8fae8736b23..c517f63165a 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -787,6 +787,7 @@ + diff --git a/src/NHibernate/Impl/MultipleQueriesCacheAssembler.cs b/src/NHibernate/Impl/MultipleQueriesCacheAssembler.cs index bfb354e5f87..e4ba00617ce 100644 --- a/src/NHibernate/Impl/MultipleQueriesCacheAssembler.cs +++ b/src/NHibernate/Impl/MultipleQueriesCacheAssembler.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using NHibernate.Cache; using NHibernate.Engine; using NHibernate.Type; @@ -28,13 +29,29 @@ public object Disassemble(object value, ISessionImplementor session, object owne var singleQueryCached = new List(); foreach (object objToCache in itemList) { - if (assemblers.Length == 1) - { - singleQueryCached.Add(assemblers[0].Disassemble(objToCache, session, owner)); - } - else + if (objToCache != null) { - singleQueryCached.Add(TypeHelper.Disassemble((object[]) objToCache, assemblers, null, session, null)); + var valuesToCache = objToCache as object[]; + var assemblersToCache = assemblers; + + if (valuesToCache != null) + { + assemblersToCache = assemblers.Where((x, index) => valuesToCache[index] != null).ToArray(); + valuesToCache = valuesToCache.Where(x => x != null).ToArray(); + } + else + { + valuesToCache = new object[] { objToCache }; + } + + if (valuesToCache.Length == 1) + { + singleQueryCached.Add(assemblers[0].Disassemble(valuesToCache[0], session, owner)); + } + else + { + singleQueryCached.Add(TypeHelper.Disassemble(valuesToCache, assemblersToCache, null, session, null)); + } } } cacheable.Add(singleQueryCached);