Skip to content

Fix collection cache lookup failure with enum keys #3693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions src/NHibernate.Test/Async/NHSpecificTest/GH3643/FixtureByCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System.Linq;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH3643
{
using System.Threading.Tasks;
using System.Threading;
[TestFixture]
public class FixtureByCodeAsync : TestCaseMappingByCode
{
protected override void Configure(Configuration configuration)
{
configuration.SetProperty(Environment.UseQueryCache, "true");
configuration.SetProperty(Environment.GenerateStatistics, "true");
}

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();

mapper.Class<Entity>(
rc =>
{
rc.Id(x => x.Id);
rc.Bag(
x => x.Children,
m =>
{
m.Access(Accessor.Field);
m.Key(k => k.Column("EntityId"));
m.Cascade(Mapping.ByCode.Cascade.All);
},
r => r.OneToMany());

rc.Cache(
cm =>
{
cm.Include(CacheInclude.All);
cm.Usage(CacheUsage.ReadWrite);
});
});

mapper.Class<ChildEntity>(
rc =>
{
rc.Id(x => x.Id);
rc.Cache(
cm =>
{
cm.Include(CacheInclude.All);
cm.Usage(CacheUsage.ReadWrite);
});
});

return mapper.CompileMappingForAllExplicitlyAddedEntities();
}

protected override void OnSetUp()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();

var entity = new Entity { Id = EntityId.Id1 };
entity.Children.Add(new ChildEntity { Id = 0 });
entity.Children.Add(new ChildEntity { Id = 1 });
session.Save(entity);

transaction.Commit();
}

protected override void OnTearDown()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();

session.CreateQuery("delete from ChildEntity").ExecuteUpdate();
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}

[Test]
public async Task LoadsEntityWithEnumIdAndChildrenUsingQueryCacheAsync()
{
await (LoadEntityWithQueryCacheAsync()); // warm up cache

var entity = await (LoadEntityWithQueryCacheAsync());

Assert.That(entity.Children.Count(), Is.EqualTo(2));

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
}

private async Task<Entity> LoadEntityWithQueryCacheAsync(CancellationToken cancellationToken = default(CancellationToken))
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();
var entity = (await (session
.Query<Entity>()
.FetchMany(x => x.Children)
.WithOptions(opt => opt.SetCacheable(true))
.ToListAsync(cancellationToken)))[0];

await (transaction.CommitAsync(cancellationToken));
return entity;
}
}
}
25 changes: 25 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH3643/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;

// ReSharper disable CollectionNeverUpdated.Local
// ReSharper disable UnassignedGetOnlyAutoProperty

namespace NHibernate.Test.NHSpecificTest.GH3643
{
class Entity
{
private readonly ICollection<ChildEntity> _children = [];
public virtual EntityId Id { get; set; }
public virtual ICollection<ChildEntity> Children => _children;
}

class ChildEntity
{
public virtual int Id { get; set; }
}

enum EntityId
{
Id1,
Id2
}
}
112 changes: 112 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH3643/FixtureByCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System.Linq;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH3643
{
[TestFixture]
public class FixtureByCode : TestCaseMappingByCode
{
protected override void Configure(Configuration configuration)
{
configuration.SetProperty(Environment.UseQueryCache, "true");
configuration.SetProperty(Environment.GenerateStatistics, "true");
}

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();

mapper.Class<Entity>(
rc =>
{
rc.Id(x => x.Id);
rc.Bag(
x => x.Children,
m =>
{
m.Access(Accessor.Field);
m.Key(k => k.Column("EntityId"));
m.Cascade(Mapping.ByCode.Cascade.All);
},
r => r.OneToMany());

rc.Cache(
cm =>
{
cm.Include(CacheInclude.All);
cm.Usage(CacheUsage.ReadWrite);
});
});

mapper.Class<ChildEntity>(
rc =>
{
rc.Id(x => x.Id);
rc.Cache(
cm =>
{
cm.Include(CacheInclude.All);
cm.Usage(CacheUsage.ReadWrite);
});
});

return mapper.CompileMappingForAllExplicitlyAddedEntities();
}

protected override void OnSetUp()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();

var entity = new Entity { Id = EntityId.Id1 };
entity.Children.Add(new ChildEntity { Id = 0 });
entity.Children.Add(new ChildEntity { Id = 1 });
session.Save(entity);

transaction.Commit();
}

protected override void OnTearDown()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();

session.CreateQuery("delete from ChildEntity").ExecuteUpdate();
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}

[Test]
public void LoadsEntityWithEnumIdAndChildrenUsingQueryCache()
{
LoadEntityWithQueryCache(); // warm up cache

var entity = LoadEntityWithQueryCache();

Assert.That(entity.Children.Count(), Is.EqualTo(2));

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
}

private Entity LoadEntityWithQueryCache()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();
var entity = session
.Query<Entity>()
.FetchMany(x => x.Children)
.WithOptions(opt => opt.SetCacheable(true))
.ToList()[0];

transaction.Commit();
return entity;
}
}
}
1 change: 1 addition & 0 deletions src/NHibernate/Async/Type/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ internal static async Task InitializeCollectionsAsync(
continue;
}

value = await (pair.Value.KeyType.AssembleAsync(value, session, null, cancellationToken)).ConfigureAwait(false);
var collection = session.PersistenceContext.GetCollection(new CollectionKey(pair.Value, value));
await (collection.ForceInitializationAsync(cancellationToken)).ConfigureAwait(false);
assembleRow[pair.Key] = collection;
Expand Down
3 changes: 2 additions & 1 deletion src/NHibernate/Type/TypeHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using NHibernate.Collection;
Expand Down Expand Up @@ -133,6 +133,7 @@ internal static void InitializeCollections(
continue;
}

value = pair.Value.KeyType.Assemble(value, session, null);
var collection = session.PersistenceContext.GetCollection(new CollectionKey(pair.Value, value));
collection.ForceInitialization();
assembleRow[pair.Key] = collection;
Expand Down