Skip to content

Commit

Permalink
Merge 5.4.4 into master
Browse files Browse the repository at this point in the history
  • Loading branch information
fredericDelaporte committed Jul 30, 2023
2 parents c4c711d + dd17017 commit d32a9d9
Show file tree
Hide file tree
Showing 23 changed files with 573 additions and 30 deletions.
25 changes: 24 additions & 1 deletion releasenotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
Build 5.4.3
Build 5.4.4
=============================

Release notes - NHibernate - Version 5.4.4

6 issues were resolved in this release.

** Bug

* #3359 2nd level cache GetMany ineffective for collections
* #3354 Invalid program generated by FieldInterceptorProxyBuilder for indexer property getter
* #3352 Fetch throws "could not resolve property" error for a property that is not mapped

** Improvement

* #3368 Allow internal entity classes/interfaces in .NET Standard 2.0 for field interceptor

** Task

* #3386 Release 5.4.4
* #3367 Update readme with actual dev build information for 5.4


Build 5.4.3
=============================

Release notes - NHibernate - Version 5.4.3
Expand Down
25 changes: 24 additions & 1 deletion src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1565,8 +1565,31 @@ public async Task QueryFetchEntityBatchCacheTestAsync(bool clearEntityCacheAfter
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
}

[Test]
public async Task CollectionLazyInitializationFromCacheIsBatchedAsync()
{
using (var s = OpenSession())
{
var readOnly = await (s.GetAsync<ReadOnly>(await (s.Query<ReadOnly>().Select(x => x.Id).FirstAsync())));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
itemCache.ClearStatistics();

using (var s = OpenSession())
{
var readOnly = await (s.GetAsync<ReadOnly>(await (s.Query<ReadOnly>().Select(x => x.Id).FirstAsync())));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
}

private async Task AssertMultipleCacheCallsAsync<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken))
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken))
where TEntity : CacheEntity
{
var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName);
Expand Down
3 changes: 3 additions & 0 deletions src/NHibernate.Test/Async/LazyProperty/LazyPropertyFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ public async Task CanGetValueForNonLazyPropertyAsync()
Assert.That(book.Name, Is.EqualTo("some name"));
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?"));
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "ALotOfText"), Is.False);
//GH-3354 Exception accessing indexer property
Assert.That(book[0], Is.EqualTo(0));
Assert.DoesNotThrow(() => book[0] = 0);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//------------------------------------------------------------------------------
// <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.MappingSchema;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH3352
{
using System.Threading.Tasks;
[TestFixture]
public class FetchFromNotMappedBaseClassFixtureAsync : TestCaseMappingByCode
{
protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<EntityNameMapped>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Name, m => m.Lazy(true));
});
mapper.Class<EntityParentMapped>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.ManyToOne(x => x.Parent, m => m.ForeignKey("none"));
});
mapper.Class<EntityComponentMapped>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Component(x => x.Component);
});
mapper.Component<Component>(rc =>
{
rc.Property(x => x.Field);
rc.ManyToOne(x => x.Entity, m => m.ForeignKey("none"));
rc.Lazy(true);
});
return mapper.CompileMappingForAllExplicitlyAddedEntities();
}

protected override void OnSetUp()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();
var np = new EntityComponentMapped { Component = new Component { Field = "x" } };
session.Save(np);
var e = new EntityParentMapped { Parent = np };
session.Save(e);
var nameMapped = new EntityNameMapped { Name = "lazy" };
session.Save(nameMapped);
np.Component.Entity = nameMapped;

transaction.Commit();
}

protected override void OnTearDown()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}

[Test]
public async Task CanFetchLazyComponentFromNotMappedBaseClassAsync()
{
using var session = OpenSession();
var list = await (session.Query<EntityComponentMapped>().Fetch(x => x.Component).ToListAsync());

Assert.That(list, Has.Count.EqualTo(1));
var result = list[0];
Assert.That(NHibernateUtil.IsPropertyInitialized(result, nameof(result.Component)));
Assert.That(result.Component.Field, Is.EqualTo("x"));
}

[Test]
public async Task CanFetchLazyComponentThenEntityFromNotMappedBaseClassAsync()
{
using var session = OpenSession();
var list = await (session.Query<EntityComponentMapped>()
.Fetch(x => x.Component)
.ThenFetch(x => x.Entity)
.ThenFetch(x => x.Name)
.ToListAsync());

Assert.That(list, Has.Count.EqualTo(1));
var result = list[0];
Assert.That(NHibernateUtil.IsPropertyInitialized(result, nameof(result.Component)));
Assert.That(result.Component.Field, Is.EqualTo("x"));
Assert.That(result.Component.Entity, Is.Not.Null);
Assert.That(NHibernateUtil.IsInitialized(result.Component.Entity), Is.True);
Assert.That(NHibernateUtil.IsPropertyInitialized(result.Component.Entity, nameof(result.Name)), Is.True);
Assert.That(result.Component.Entity.Name, Is.EqualTo("lazy"));
}

[Test]
public async Task CanFetchLazyPropertyFromNotMappedBaseClassAsync()
{
using var session = OpenSession();
var list = await (session.Query<EntityNameMapped>().Fetch(x => x.Name).ToListAsync());

Assert.That(list, Has.Count.EqualTo(1));
var result = list[0];
Assert.That(NHibernateUtil.IsPropertyInitialized(result, nameof(result.Name)));
Assert.That(result.Name, Is.EqualTo("lazy"));
}

[Test]
public async Task CanThenFetchLazyComponentFromNotMappedBaseClassAsync()
{
using var session = OpenSession();
var list = await (session.Query<EntityParentMapped>().Fetch(x => x.Parent).ThenFetch(x => x.Component).ToListAsync());

Assert.That(list, Has.Count.EqualTo(1));
var result = list[0].Parent;
Assert.That(NHibernateUtil.IsInitialized(result), Is.True);
Assert.That(NHibernateUtil.IsPropertyInitialized(result, nameof(result.Component)));
Assert.That(result.Component.Field, Is.EqualTo("x"));
}

[KnownBug("GH-3356")]
[Test(Description = "GH-3356" )]
public async Task FetchAfterSelectAsync()
{
using var log = new SqlLogSpy();

using var s = OpenSession();
var list = await (s.Query<EntityParentMapped>()
.Select(x => x.Parent)
.Fetch(x => x.Component)
.ThenFetch(x => x.Entity)
.ThenFetch(x => x.Name)
.ToListAsync());
Assert.That(list, Has.Count.EqualTo(1));
var result = list[0];
Assert.That(NHibernateUtil.IsPropertyInitialized(result, nameof(result.Component)));
Assert.That(result.Component.Field, Is.EqualTo("x"));
Assert.That(result.Component.Entity, Is.Not.Null);
Assert.That(NHibernateUtil.IsInitialized(result.Component.Entity), Is.True);
Assert.That(NHibernateUtil.IsPropertyInitialized(result.Component.Entity, nameof(result.Name)), Is.True);
Assert.That(result.Component.Entity.Name, Is.EqualTo("lazy"));
}

[Test]
public async Task CanFetchEntityFromNotMappedBaseClassAsync()
{
using var session = OpenSession();
var list = await (session.Query<EntityParentMapped>().Fetch(x => x.Parent).ToListAsync());

Assert.That(list, Has.Count.EqualTo(1));
Assert.That(list[0].Parent, Is.Not.Null);
Assert.That(NHibernateUtil.IsInitialized(list[0].Parent));
}

[Test]
public void FetchNotMappedAssociationThrowsAsync()
{
using var session = OpenSession();
var query = session.Query<EntityNameMapped>().Fetch(x => x.Parent);

Assert.ThrowsAsync<QueryException>(() => query.ToListAsync());
}
}
}
25 changes: 24 additions & 1 deletion src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,8 +1553,31 @@ public void QueryFetchEntityBatchCacheTest(bool clearEntityCacheAfterQuery, bool
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
}

[Test]
public void CollectionLazyInitializationFromCacheIsBatched()
{
using (var s = OpenSession())
{
var readOnly = s.Get<ReadOnly>(s.Query<ReadOnly>().Select(x => x.Id).First());
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
itemCache.ClearStatistics();

using (var s = OpenSession())
{
var readOnly = s.Get<ReadOnly>(s.Query<ReadOnly>().Select(x => x.Id).First());
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
}

private void AssertMultipleCacheCalls<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null)
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null)
where TEntity : CacheEntity
{
var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName);
Expand Down
6 changes: 6 additions & 0 deletions src/NHibernate.Test/LazyProperty/Book.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,11 @@ public virtual byte[] NoSetterImage
public virtual string FieldInterceptor { get; set; }

public virtual IList<Word> Words { get; set; }

public virtual int this[int i]
{
get { return i;}
set { }
}
}
}
3 changes: 3 additions & 0 deletions src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ public void CanGetValueForNonLazyProperty()
Assert.That(book.Name, Is.EqualTo("some name"));
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?"));
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "ALotOfText"), Is.False);
//GH-3354 Exception accessing indexer property
Assert.That(book[0], Is.EqualTo(0));
Assert.DoesNotThrow(() => book[0] = 0);
}
}

Expand Down
31 changes: 31 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH3352/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;

namespace NHibernate.Test.NHSpecificTest.GH3352
{
public class Entity
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual EntityComponentMapped Parent { get; set; }
public virtual Component Component { get; set; }
}

public class EntityNameMapped : Entity
{
}

public class EntityParentMapped : Entity
{
}

public class EntityComponentMapped : Entity
{
}

public class Component
{
public string Field { get; set; }

public EntityNameMapped Entity { get; set; }
}
}
Loading

0 comments on commit d32a9d9

Please sign in to comment.