...
 
Commits (17)
    https://gitcode.net/jobily/nhibernate-core/-/commit/2cd54e6a835d9179d7f4bdf38c94d6de846efd65 Fix fetching properties from unmapped base class (#3357) 2023-07-13T10:57:22+03:00 Roman Artiukhin bahusdrive@gmail.com Fixes #3352 https://gitcode.net/jobily/nhibernate-core/-/commit/8c0a664ee5bde1de2e496aebdc3ec64abcc59f77 Fix exception accessing indexer property for field interceptor proxy (#3358) 2023-07-13T16:42:14+03:00 Roman Artiukhin bahusdrive@gmail.com Fixes #3354 https://gitcode.net/jobily/nhibernate-core/-/commit/9302ad467bba443176f2bfb6b85855c1d1e1e504 Update NHibernate.props 2023-07-13T22:45:24+03:00 Roman Artiukhin bahusdrive@gmail.com Enable 5.4.4 dev builds https://gitcode.net/jobily/nhibernate-core/-/commit/f3bd74ef8d90675a756e54ccd956c0ef24bf6a33 Call BeforeAssemble on persistent collection InitializeFromCache to allow bat... 2023-07-18T22:30:52+10:00 Alex Zaytsev hazzik@gmail.com Fixes #3359 https://gitcode.net/jobily/nhibernate-core/-/commit/3186197c1eec77aae46eb0f2ce8cf37bb4c2a03b Migrate dev NuGet packages to Cloudsmith (#3367) 2023-07-18T22:32:43+10:00 Roman Artiukhin bahusdrive@gmail.com (cherry picked from commit <a href="/jobily/nhibernate-core/-/commit/26b557fc38824e812527f18cd5cbe3f40fab5398" data-original="26b557fc38824e812527f18cd5cbe3f40fab5398" data-link="false" data-link-reference="false" data-project="321184" data-commit="26b557fc38824e812527f18cd5cbe3f40fab5398" data-reference-type="commit" data-container="body" data-placement="top" title="Migrate dev nuget packages to Cloudsmith (#3275)" class="gfm gfm-commit has-tooltip">26b557fc</a>) https://gitcode.net/jobily/nhibernate-core/-/commit/13565fb13db643b2f64d8f407ae19b71e3e9af00 Allow internal entity classess/interfaces in .NET Standard 2.0 for field inte... 2023-07-19T13:19:11+10:00 Roman Artiukhin bahusdrive@gmail.com https://gitcode.net/jobily/nhibernate-core/-/commit/c9d0a951edaaaecfe591b806bc72bb0aa30fbb7c Fix BeforeAssemble call for Map and IdentifierBag (#3380) 2023-07-25T05:03:46+03:00 Roman Artiukhin bahusdrive@gmail.com Fixup for #3365 https://gitcode.net/jobily/nhibernate-core/-/commit/c4c711d0aad46da4e058ef1d2006304971b75506 Improve path rule handling with reserved words in Hql.g (#3384) 2023-07-28T08:17:25+03:00 Roman Artiukhin bahusdrive@gmail.com Avoids handled exception when parsing queries with reserved words in path. https://gitcode.net/jobily/nhibernate-core/-/commit/dd17017b658adc1e90bfba0d2bfea2fb5d9518f5 Release 5.4.4 (#3386) 2023-07-30T20:52:55+02:00 Frédéric Delaporte 12201973+fredericDelaporte@users.noreply.github.com https://gitcode.net/jobily/nhibernate-core/-/commit/d32a9d9370c40fba9c54b2c39864aa2a8ba0d94d Merge 5.4.4 into master 2023-07-30T20:58:33+02:00 Frédéric Delaporte 12201973+fredericdelaporte@users.noreply.github.com https://gitcode.net/jobily/nhibernate-core/-/commit/d6fd1dcedff65571baf7e8b59dc4b88c508d312b Enable Not node handling in HqlParser.NegateNode (#3390) 2023-08-01T16:52:01+03:00 Roman Artiukhin bahusdrive@gmail.com https://gitcode.net/jobily/nhibernate-core/-/commit/494917424e599e702d2c0540c10a8fa9065f6448 Use table group joins for many-to-many in Criteria and Entity loaders (#2687) 2023-08-01T22:23:04+03:00 Roman Artiukhin bahusdrive@gmail.com Possible breaking change: Default not-found behavior now works correctly for many-to-many in Criteria so now it throws ObjectNotFoundException exception on many-to-many Criteria fetch for not found records. https://gitcode.net/jobily/nhibernate-core/-/commit/782f8d7f3e521343c177f7c3289b2fff15f1b6c8 Clean up Linq Joiner class removing no longer needed AddJoinMethod property (... 2023-08-02T09:30:24+03:00 Roman Artiukhin bahusdrive@gmail.com Obsoleted by #2539 https://gitcode.net/jobily/nhibernate-core/-/commit/8f05f67e808dc2fc503d4fd0d94b6c9cb66429a1 Partial fix fetching lazy property after Select in Linq (#3392) 2023-08-04T10:05:04+03:00 Roman Artiukhin bahusdrive@gmail.com Partial fix of #3356 Fix wrong hql From node detection logic. Query source detection is still broken. https://gitcode.net/jobily/nhibernate-core/-/commit/a7deeb068a82fea9c2826856bc9d95d291856341 Get rid of select queries for each ManyToMany not found ignored element in hq... 2023-08-04T23:33:34+03:00 Roman Artiukhin bahusdrive@gmail.com https://gitcode.net/jobily/nhibernate-core/-/commit/2d774801f88790706bf39fb478881b746a59f501 Remove hql ConstantConverter (#3395) 2023-08-06T16:30:29+03:00 Roman Artiukhin bahusdrive@gmail.com Removed logic handled in DotNode class (see LiteralProcessor.LookupConstant) https://gitcode.net/jobily/nhibernate-core/-/commit/d1a7e10177ee88f2421b916dd5a48563d556d253 Get rid of select queries for each ManyToMany not found ignored element in... 2023-08-09T09:53:19+03:00 Roman Artiukhin bahusdrive@gmail.com Get rid of select queries for each ManyToMany not found ignored element in Criteria and lazy loading (#3396)
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 Release notes - NHibernate - Version 5.4.3
......
...@@ -1565,8 +1565,31 @@ public async Task QueryFetchEntityBatchCacheTestAsync(bool clearEntityCacheAfter ...@@ -1565,8 +1565,31 @@ public async Task QueryFetchEntityBatchCacheTestAsync(bool clearEntityCacheAfter
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count"); 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, 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 where TEntity : CacheEntity
{ {
var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName); var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName);
......
...@@ -186,6 +186,18 @@ public async Task TestLinqFetchPropertyAsync() ...@@ -186,6 +186,18 @@ public async Task TestLinqFetchPropertyAsync()
AssertFetchProperty(person); AssertFetchProperty(person);
} }
[Test]
public async Task TestLinqFetchPropertyAfterSelectAsync()
{
using var s = OpenSession();
var owner = await (s.Query<Animal>()
.Select(a => a.Owner)
.Fetch(o => o.Image)
.FirstOrDefaultAsync(o => o.Id == 1));
AssertFetchProperty(owner);
}
private static void AssertFetchProperty(Person person) private static void AssertFetchProperty(Person person)
{ {
Assert.That(person, Is.Not.Null); Assert.That(person, Is.Not.Null);
......
...@@ -230,6 +230,9 @@ public async Task CanGetValueForNonLazyPropertyAsync() ...@@ -230,6 +230,9 @@ public async Task CanGetValueForNonLazyPropertyAsync()
Assert.That(book.Name, Is.EqualTo("some name")); Assert.That(book.Name, Is.EqualTo("some name"));
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?")); Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?"));
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "ALotOfText"), Is.False); 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);
} }
} }
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
using System.Linq; using System.Linq;
using NHibernate.Criterion; using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Linq; using NHibernate.Linq;
using NHibernate.SqlCommand; using NHibernate.SqlCommand;
using NHibernate.Transform; using NHibernate.Transform;
...@@ -20,7 +19,7 @@ namespace NHibernate.Test.NHSpecificTest.GH1994 ...@@ -20,7 +19,7 @@ namespace NHibernate.Test.NHSpecificTest.GH1994
{ {
using System.Threading.Tasks; using System.Threading.Tasks;
[TestFixture] [TestFixture]
public class FixtureAsync : BugTestCase public class ManyToManyFilteredFixtureAsync : BugTestCase
{ {
protected override void OnSetUp() protected override void OnSetUp()
{ {
...@@ -41,14 +40,7 @@ protected override void OnTearDown() ...@@ -41,14 +40,7 @@ protected override void OnTearDown()
using (var session = OpenSession()) using (var session = OpenSession())
using (var transaction = session.BeginTransaction()) using (var transaction = session.BeginTransaction())
{ {
// The HQL delete does all the job inside the database without loading the entities, but it does
// not handle delete order for avoiding violating constraints if any. Use
// session.Delete("from System.Object");
// instead if in need of having NHibernate ordering the deletes, but this will cause
// loading the entities in the session.
session.Delete("from System.Object"); session.Delete("from System.Object");
transaction.Commit(); transaction.Commit();
} }
} }
...@@ -70,9 +62,6 @@ public async Task TestUnfilteredLinqQueryAsync() ...@@ -70,9 +62,6 @@ public async Task TestUnfilteredLinqQueryAsync()
[Test] [Test]
public async Task TestFilteredByWhereCollectionLinqQueryAsync() public async Task TestFilteredByWhereCollectionLinqQueryAsync()
{ {
if(Dialect is PostgreSQLDialect)
Assert.Ignore("Dialect doesn't support 0/1 to bool implicit cast");
using (var s = OpenSession()) using (var s = OpenSession())
{ {
var query = await (s.Query<Asset>() var query = await (s.Query<Asset>()
...@@ -150,5 +139,31 @@ public async Task TestQueryOverRestrictionWithClauseAsync() ...@@ -150,5 +139,31 @@ public async Task TestQueryOverRestrictionWithClauseAsync()
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents"); Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
} }
} }
[Test]
public async Task LazyLoadAsync()
{
using (var s = OpenSession())
{
var asset = await (s.Query<Asset>().FirstAsync());
Assert.That(asset.Documents.Count, Is.EqualTo(2));
Assert.That(asset.DocumentsBag.Count, Is.EqualTo(2));
Assert.That(asset.DocumentsFiltered.Count, Is.EqualTo(1));
}
}
[Test]
public async Task LazyLoadFilteredAsync()
{
using (var s = OpenSession())
{
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
var asset = await (s.Query<Asset>().FirstAsync());
Assert.That(asset.Documents.Count, Is.EqualTo(1));
Assert.That(asset.DocumentsBag.Count, Is.EqualTo(1));
Assert.That(asset.DocumentsFiltered.Count, Is.EqualTo(1));
}
}
} }
} }
...@@ -59,5 +59,21 @@ public async Task NotIsCorrectlyHandledAsync() ...@@ -59,5 +59,21 @@ public async Task NotIsCorrectlyHandledAsync()
)"); )");
Assert.That((await (q.ListAsync()))[0], Is.EqualTo(0)); Assert.That((await (q.ListAsync()))[0], Is.EqualTo(0));
} }
[Test]
public async Task NotNotExistsNegatedAsync()
{
using var log = new SqlLogSpy();
using var session = OpenSession();
var results = await (session.CreateQuery(
@"SELECT COUNT(ROOT.Id)
FROM Entity AS ROOT
WHERE NOT (
NOT EXISTS (FROM ChildEntity AS CHILD WHERE CHILD.Parent = ROOT)
AND NOT ROOT.Name = 'Parent'
)").ListAsync());
Assert.That(log.GetWholeLog(), Does.Not.Contains(" NOT ").IgnoreCase);
Assert.That(results.Count, Is.EqualTo(1));
}
} }
} }
//------------------------------------------------------------------------------
// <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());
}
}
}
//------------------------------------------------------------------------------
// <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;
using NHibernate.Cfg;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
using System.Threading.Tasks;
[TestFixture]
public class FixtureAsync : BugTestCase
{
protected override void OnTearDown()
{
using (ISession s = Sfi.OpenSession())
{
s.Delete("from Device");
s.Delete("from Drive");
s.Flush();
}
}
protected override void Configure(Configuration configuration)
{
configuration.SetProperty(Cfg.Environment.UseSecondLevelCache, "false");
base.Configure(configuration);
}
[Test]
public async Task DeviceOfDriveAsync()
{
int[] dvSavedId = new int[2];
Drive dr1 = new Drive("Drive 1");
Drive dr2 = new Drive("Drive 2");
Drive dr3 = new Drive("Drive 3");
Device dv1 = new Device("Device 1");
Device dv2 = new Device("Device 2");
using (ISession s = Sfi.OpenSession())
{
await (s.SaveAsync(dr1));
await (s.SaveAsync(dr2));
await (s.SaveAsync(dr3));
dvSavedId[0] = (int) await (s.SaveAsync(dv1));
dvSavedId[1] = (int) await (s.SaveAsync(dv2));
await (s.FlushAsync());
}
dv1.Drives.Add(dr1);
dv1.Drives.Add(dr2);
dv2.Drives.Add(dr1);
dv2.Drives.Add(dr3);
using (ISession s = Sfi.OpenSession())
{
dvSavedId[0] = (int) await (s.SaveAsync(dv1));
dvSavedId[1] = (int) await (s.SaveAsync(dv2));
await (s.FlushAsync());
}
dv1 = null;
dv2 = null;
using (ISession s = Sfi.OpenSession())
{
await (s.DeleteAsync(dr3));
await (s.FlushAsync());
dv1 = (Device) await (s.LoadAsync(typeof(Device), dvSavedId[0]));
dv2 = (Device) await (s.LoadAsync(typeof(Device), dvSavedId[1]));
}
Assert.AreEqual(2, dv1.Drives.Count);
// Verify one is missing
Assert.AreEqual(1, dv2.Drives.Count);
// Verify dv1 unchanged
Assert.IsTrue(dv1.Drives.Contains(dr1));
Assert.IsTrue(dv1.Drives.Contains(dr2));
// Verify dv2
Assert.IsTrue(dv2.Drives.Contains(dr1));
Assert.IsFalse(dv2.Drives.Contains(dr3));
//Make sure that flush didn't touch not-found="ignore" records for not modified collection
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = await (s.GetAsync<Device>(dv2.Id));
await (s.FlushAsync());
await (t.CommitAsync());
}
await (VerifyResultAsync(expectedInCollection: 1, expectedInDb: 2, msg: "not modified collection"));
//Many-to-many clears collection and recreates it so not-found ignore records are lost
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = await (s.GetAsync<Device>(dv2.Id));
dv2.Drives.Add(dr2);
await (t.CommitAsync());
}
await (VerifyResultAsync(2, 2, msg: "modified collection"));
async Task VerifyResultAsync(int expectedInCollection, int expectedInDb, string msg)
{
using (var s = Sfi.OpenSession())
{
var realCound = Convert.ToInt32(
await (s.CreateSQLQuery("select count(*) from DriveOfDevice where DeviceId = :id ")
.SetParameter("id", dv2.Id)
.UniqueResultAsync<object>()));
dv2 = await (s.GetAsync<Device>(dv2.Id));
Assert.That(dv2.Drives.Count, Is.EqualTo(expectedInCollection), msg);
Assert.That(realCound, Is.EqualTo(expectedInDb), msg);
}
}
}
}
}
//------------------------------------------------------------------------------
// <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;
using NHibernate.Criterion;
using NHibernate.Transform;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
using System.Threading.Tasks;
[TestFixture(0)]
[TestFixture(1)]
[TestFixture(2)]
public class ManyToManyFixtureAsync : BugTestCase
{
private int id2;
private readonly int _drivesCount;
private int _withTemplateId;
private int ValidDrivesCount => _drivesCount;
public ManyToManyFixtureAsync(int drivesCount)
{
_drivesCount = drivesCount;
}
protected override void OnSetUp()
{
Drive dr1 = new Drive("Drive 1");
Drive dr2 = new Drive("Drive 2");
Drive dr3 = new Drive("Drive 3");
Device dv1 = new Device("Device 1");
Device dv2 = new Device("Device 2");
var withTemplate = new Device("Device With Device 2 template") { Template = dv2 };
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.Save(dr1);
s.Save(dr2);
s.Save(dr3);
AddDrive(dv1, dr2);
AddDrive(dv1, dr1);
AddDrive(dv2, dr3);
AddDrive(dv2, dr1);
s.Save(dv1);
id2 = (int) s.Save(dv2);
_withTemplateId = (int)s.Save(withTemplate);
t.Commit();
}
private void AddDrive(Device dv, Drive drive)
{
if(dv.DrivesNotIgnored.Count >= _drivesCount)
return;
dv.DrivesNotIgnored.Add(drive);
}
protected override void OnTearDown()
{
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.CreateSQLQuery("delete from DriveOfDevice").ExecuteUpdate();
s.Delete("from Device");
s.Delete("from Drive");
t.Commit();
}
[Test]
public async Task QueryOverFetchAsync()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = await (s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.DrivesNotIgnored)
.Where(Restrictions.IdEq(id2))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefaultAsync());
Assert.That(NHibernateUtil.IsInitialized(dv2.DrivesNotIgnored), Is.True);
Assert.That(dv2.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task QueryOverFetch2Async()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = await (s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.Template, x => x.Template.DrivesNotIgnored)
.Where(Restrictions.IdEq(_withTemplateId))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefaultAsync());
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.DrivesNotIgnored), Is.True);
Assert.That(withTemplate.Template.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task HqlFetchAsync()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = await (s.CreateQuery("from Device d left join fetch d.DrivesNotIgnored where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResultAsync<Device>());
Assert.That(NHibernateUtil.IsInitialized(dv2.DrivesNotIgnored), Is.True);
Assert.That(dv2.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task HqlFetch2Async()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = await (s.CreateQuery("from Device t left join fetch t.Template d left join fetch d.DrivesNotIgnored where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResultAsync<Device>());
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.DrivesNotIgnored), Is.True);
Assert.That(withTemplate.Template.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task LazyLoadAsync()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = await (s.GetAsync<Device>(id2));
await (NHibernateUtil.InitializeAsync(dv2.DrivesNotIgnored));
Assert.That(NHibernateUtil.IsInitialized(dv2.DrivesNotIgnored), Is.True);
Assert.That(dv2.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
// First query for Device, second for Drives collection
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(2));
}
}
}
//------------------------------------------------------------------------------
// <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;
using NHibernate.Criterion;
using NHibernate.Transform;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
using System.Threading.Tasks;
[TestFixture(0)]
[TestFixture(1)]
[TestFixture(2)]
public class ManyToManyNotFoundIgnoreFixtureAsync : BugTestCase
{
private int id1;
private int id2;
private int _drive2Id;
private int _withTemplateId;
private readonly int _drivesCount;
private int ValidDrivesCount => _drivesCount == 0 ? 0 : _drivesCount - 1;
public ManyToManyNotFoundIgnoreFixtureAsync(int drivesCount)
{
_drivesCount = drivesCount;
}
protected override void OnSetUp()
{
Drive dr1 = new Drive("Drive 1");
Drive dr2 = new Drive("Drive 2");
Drive dr3 = new Drive("Drive 3");
Device dv1 = new Device("Device 1");
Device dv2 = new Device("Device 2");
var withTemplate = new Device("Device With Device 2 template") { Template = dv2 };
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.Save(dr1);
_drive2Id = (int)s.Save(dr2);
s.Save(dr3);
AddDrive(dv1, dr2);
AddDrive(dv1, dr1);
AddDrive(dv2, dr3);
AddDrive(dv2, dr1);
id1 = (int) s.Save(dv1);
id2 = (int) s.Save(dv2);
_withTemplateId = (int)s.Save(withTemplate);
s.Flush();
s.Clear();
s.Delete(dr3);
t.Commit();
}
private void AddDrive(Device dv, Drive drive)
{
if(dv.Drives.Count >= _drivesCount)
return;
dv.Drives.Add(drive);
}
protected override void OnTearDown()
{
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.CreateSQLQuery("delete from DriveOfDevice").ExecuteUpdate();
s.Delete("from Device");
s.Delete("from Drive");
t.Commit();
}
[Test]
public async Task DeviceOfDriveAsync()
{
Device dv1;
Device dv2;
using (ISession s = Sfi.OpenSession())
{
dv1 = (Device) await (s.LoadAsync(typeof(Device), id1));
dv2 = (Device) await (s.LoadAsync(typeof(Device), id2));
await (NHibernateUtil.InitializeAsync(dv1.Drives));
await (NHibernateUtil.InitializeAsync(dv2.Drives));
}
Assert.That(dv1.Drives, Has.Count.EqualTo(_drivesCount).And.None.Null);
// Verify one is missing
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
//Make sure that flush didn't touch not-found="ignore" records for not modified collection
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = await (s.GetAsync<Device>(dv2.Id));
await (s.FlushAsync());
await (t.CommitAsync());
}
await (VerifyResultAsync(expectedInCollection: ValidDrivesCount, expectedInDb: _drivesCount, msg: "not modified collection"));
// Many-to-many clears collection and recreates it so not-found ignore records are lost
// Note: It's not the case when no valid records are present, so loaded Drives collection is empty
// Just skip this check in this case:
if (_drivesCount < 2)
return;
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = await (s.GetAsync<Device>(dv2.Id));
dv2.Drives.Add(await (s.LoadAsync<Drive>(_drive2Id)));
await (t.CommitAsync());
}
await (VerifyResultAsync(_drivesCount, _drivesCount, msg: "modified collection"));
async Task VerifyResultAsync(int expectedInCollection, int expectedInDb, string msg)
{
using (var s = Sfi.OpenSession())
{
var realCound = Convert.ToInt32(
await (s.CreateSQLQuery("select count(*) from DriveOfDevice where DeviceId = :id ")
.SetParameter("id", dv2.Id)
.UniqueResultAsync<object>()));
dv2 = await (s.GetAsync<Device>(dv2.Id));
Assert.That(dv2.Drives.Count, Is.EqualTo(expectedInCollection), msg);
Assert.That(realCound, Is.EqualTo(expectedInDb), msg);
}
}
}
[Test]
public async Task QueryOverFetchAsync()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = await (s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.Drives)
.Where(Restrictions.IdEq(id2))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefaultAsync());
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task QueryOverFetch2Async()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = await (s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.Template, x => x.Template.Drives)
.Where(Restrictions.IdEq(_withTemplateId))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefaultAsync());
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.Drives), Is.True);
Assert.That(withTemplate.Template.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task HqlFetchAsync()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = await (s.CreateQuery("from Device d left join fetch d.Drives where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResultAsync<Device>());
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task HqlFetch2Async()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = await (s.CreateQuery("from Device t left join fetch t.Template d left join fetch d.Drives where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResultAsync<Device>());
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.Drives), Is.True);
Assert.That(withTemplate.Template.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public async Task LazyLoadAsync()
{
using var s = OpenSession();
var dv2 = await (s.GetAsync<Device>(id2));
using var log = new SqlLogSpy();
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
}
}
//------------------------------------------------------------------------------
// <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.Criterion;
using NHibernate.Linq;
using NHibernate.Transform;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
using System.Threading.Tasks;
[TestFixture]
public class ManyToManyThrowsForNotFoundFixtureAsync : BugTestCase
{
private int _id;
private int _withTemplateId;
protected override void OnSetUp()
{
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
Device dv = new Device("Device");
Drive dr = new Drive("Drive");
var withTemplate = new Device("Device With Device 2 template") { Template = dv };
s.Save(dr);
dv.DrivesNotIgnored.Add(dr);
_id = (int) s.Save(dv);
_withTemplateId = (int)s.Save(withTemplate);
s.Flush();
s.Clear();
s.Delete(dr);
t.Commit();
}
protected override void OnTearDown()
{
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
s.Delete("from Device");
s.Delete("from Drive");
t.Commit();
}
}
[Test]
public async Task LazyLoadAsync()
{
using var s = OpenSession();
var device = await (s.GetAsync<Device>(_id));
Assert.ThrowsAsync<ObjectNotFoundException>(() => NHibernateUtil.InitializeAsync(device.DrivesNotIgnored));
}
[Test]
public void QueryOverFetchAsync()
{
using var s = OpenSession();
var queryOver = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.DrivesNotIgnored)
.Where(Restrictions.IdEq(_id))
.TransformUsing(Transformers.DistinctRootEntity);
Assert.ThrowsAsync<ObjectNotFoundException>(() => queryOver.SingleOrDefaultAsync());
}
[Test]
public void QueryOverFetch2Async()
{
using var s = OpenSession();
var queryOver = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x=> x.Template, x => x.Template.DrivesNotIgnored)
.Where(Restrictions.IdEq(_withTemplateId))
.TransformUsing(Transformers.DistinctRootEntity);
Assert.ThrowsAsync<ObjectNotFoundException>(() => queryOver.SingleOrDefaultAsync());
}
[Test]
public void LinqFetchAsync()
{
using var s = OpenSession();
var query = s.Query<Device>()
.Fetch(x => x.DrivesNotIgnored)
.Where(x => x.Id == _id);
Assert.ThrowsAsync<ObjectNotFoundException>(async () => await (NHibernateUtil.InitializeAsync(await (query.SingleOrDefaultAsync()))));
}
[Test]
public void LinqFetch2Async()
{
using var s = OpenSession();
var query = s.Query<Device>()
.Fetch(x => x.Template)
.ThenFetchMany(x => x.DrivesNotIgnored)
.Where(x => x.Id == _withTemplateId);
Assert.ThrowsAsync<ObjectNotFoundException>(async () => await (NHibernateUtil.InitializeAsync(await (query.SingleOrDefaultAsync()))));
}
}
}
...@@ -8,20 +8,22 @@ ...@@ -8,20 +8,22 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using System.Linq;
using NHibernate.Criterion; using NHibernate.Criterion;
using NHibernate.Linq;
using NUnit.Framework; using NUnit.Framework;
namespace NHibernate.Test.PropertyRef namespace NHibernate.Test.PropertyRef
{ {
using System.Threading.Tasks; using System.Threading.Tasks;
[TestFixture] [TestFixture(Description = "NH-2180 (GH-1214)")]
public class ManyToManyPropertyRefFixtureAsync : TestCase public class ManyToManyPropertyRefFixtureAsync : TestCase
{ {
protected override string[] Mappings => new[] { "PropertyRef.ManyToManyWithPropertyRef.hbm.xml" }; protected override string[] Mappings => new[] { "PropertyRef.ManyToManyWithPropertyRef.hbm.xml" };
protected override string MappingsAssembly => "NHibernate.Test"; protected override string MappingsAssembly => "NHibernate.Test";
private object _manyAId; private long _manyAId;
protected override void OnSetUp() protected override void OnSetUp()
{ {
...@@ -34,7 +36,7 @@ protected override void OnSetUp() ...@@ -34,7 +36,7 @@ protected override void OnSetUp()
var manyB2 = new ManyB { Number = 8, Value = "a value of b2" }; var manyB2 = new ManyB { Number = 8, Value = "a value of b2" };
var manyB3 = new ManyB { Number = 12, Value = "a value of b3" }; var manyB3 = new ManyB { Number = 12, Value = "a value of b3" };
_manyAId = session.Save(manyA); _manyAId = (long) session.Save(manyA);
session.Save(manyB1); session.Save(manyB1);
session.Save(manyB2); session.Save(manyB2);
session.Save(manyB3); session.Save(manyB3);
...@@ -144,5 +146,20 @@ bei System.ThrowHelper.ThrowKeyNotFoundException() ...@@ -144,5 +146,20 @@ bei System.ThrowHelper.ThrowKeyNotFoundException()
Assert.That(loadedManyA.ManyBs, Has.Count.EqualTo(3).And.None.Null); Assert.That(loadedManyA.ManyBs, Has.Count.EqualTo(3).And.None.Null);
} }
[Test]
public async Task LinqFetchAsync()
{
using (var session = OpenSession())
{
var manyA = (await (session
.Query<ManyA>()
.Where(a => a.Id == _manyAId)
.FetchMany(a => a.ManyBs)
.ToListAsync()))
.First();
Assert.That(manyA.ManyBs, Has.Count.EqualTo(3).And.None.Null);
}
}
} }
} }
...@@ -1553,8 +1553,31 @@ public void QueryFetchEntityBatchCacheTest(bool clearEntityCacheAfterQuery, bool ...@@ -1553,8 +1553,31 @@ public void QueryFetchEntityBatchCacheTest(bool clearEntityCacheAfterQuery, bool
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count"); 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, 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 where TEntity : CacheEntity
{ {
var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName); var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName);
......
...@@ -175,6 +175,18 @@ public void TestLinqFetchProperty() ...@@ -175,6 +175,18 @@ public void TestLinqFetchProperty()
AssertFetchProperty(person); AssertFetchProperty(person);
} }
[Test]
public void TestLinqFetchPropertyAfterSelect()
{
using var s = OpenSession();
var owner = s.Query<Animal>()
.Select(a => a.Owner)
.Fetch(o => o.Image)
.FirstOrDefault(o => o.Id == 1);
AssertFetchProperty(owner);
}
private static void AssertFetchProperty(Person person) private static void AssertFetchProperty(Person person)
{ {
Assert.That(person, Is.Not.Null); Assert.That(person, Is.Not.Null);
......
using Antlr.Runtime;
using NHibernate.Hql.Ast.ANTLR;
using NHibernate.Hql.Ast.ANTLR.Tree;
using NUnit.Framework;
namespace NHibernate.Test.Hql.Parser
{
[TestFixture]
public class HqlParserFixture
{
[Test]
public void HandlesPathWithReservedWords()
{
Assert.DoesNotThrow(() => Parse("delete from System.Object"));
Assert.DoesNotThrow(() => Parse("delete from Object.Object.Object.Object"));
}
private static void Parse(string hql)
{
var lex = new HqlLexer(new CaseInsensitiveStringStream(hql));
var tokens = new CommonTokenStream(lex);
var parser = new HqlParser(tokens)
{
TreeAdaptor = new ASTTreeAdaptor(),
ParseErrorHandler = new WarningAsErrorReporter()
}.statement();
}
}
}
using Antlr.Runtime;
using NHibernate.Hql.Ast.ANTLR;
namespace NHibernate.Test.Hql.Parser
{
public class WarningAsErrorReporter : IParseErrorHandler
{
public void ReportError(RecognitionException e)
{
throw e;
}
public void ReportError(string s)
{
throw new QueryException(s);
}
public void ReportWarning(string s)
{
throw new QueryException(s);
}
public int GetErrorCount() => 0;
public void ThrowQueryException()
{
}
}
}
...@@ -28,5 +28,11 @@ public virtual byte[] NoSetterImage ...@@ -28,5 +28,11 @@ public virtual byte[] NoSetterImage
public virtual string FieldInterceptor { get; set; } public virtual string FieldInterceptor { get; set; }
public virtual IList<Word> Words { get; set; } public virtual IList<Word> Words { get; set; }
public virtual int this[int i]
{
get { return i;}
set { }
}
} }
} }
...@@ -225,6 +225,9 @@ public void CanGetValueForNonLazyProperty() ...@@ -225,6 +225,9 @@ public void CanGetValueForNonLazyProperty()
Assert.That(book.Name, Is.EqualTo("some name")); Assert.That(book.Name, Is.EqualTo("some name"));
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?")); Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?"));
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "ALotOfText"), Is.False); 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);
} }
} }
......
using System.Linq; using System.Linq;
using NHibernate.Criterion; using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Linq; using NHibernate.Linq;
using NHibernate.SqlCommand; using NHibernate.SqlCommand;
using NHibernate.Transform; using NHibernate.Transform;
...@@ -9,7 +8,7 @@ ...@@ -9,7 +8,7 @@
namespace NHibernate.Test.NHSpecificTest.GH1994 namespace NHibernate.Test.NHSpecificTest.GH1994
{ {
[TestFixture] [TestFixture]
public class Fixture : BugTestCase public class ManyToManyFilteredFixture : BugTestCase
{ {
protected override void OnSetUp() protected override void OnSetUp()
{ {
...@@ -30,14 +29,7 @@ protected override void OnTearDown() ...@@ -30,14 +29,7 @@ protected override void OnTearDown()
using (var session = OpenSession()) using (var session = OpenSession())
using (var transaction = session.BeginTransaction()) using (var transaction = session.BeginTransaction())
{ {
// The HQL delete does all the job inside the database without loading the entities, but it does
// not handle delete order for avoiding violating constraints if any. Use
// session.Delete("from System.Object");
// instead if in need of having NHibernate ordering the deletes, but this will cause
// loading the entities in the session.
session.Delete("from System.Object"); session.Delete("from System.Object");
transaction.Commit(); transaction.Commit();
} }
} }
...@@ -59,9 +51,6 @@ public void TestUnfilteredLinqQuery() ...@@ -59,9 +51,6 @@ public void TestUnfilteredLinqQuery()
[Test] [Test]
public void TestFilteredByWhereCollectionLinqQuery() public void TestFilteredByWhereCollectionLinqQuery()
{ {
if(Dialect is PostgreSQLDialect)
Assert.Ignore("Dialect doesn't support 0/1 to bool implicit cast");
using (var s = OpenSession()) using (var s = OpenSession())
{ {
var query = s.Query<Asset>() var query = s.Query<Asset>()
...@@ -139,5 +128,31 @@ public void TestQueryOverRestrictionWithClause() ...@@ -139,5 +128,31 @@ public void TestQueryOverRestrictionWithClause()
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents"); Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
} }
} }
[Test]
public void LazyLoad()
{
using (var s = OpenSession())
{
var asset = s.Query<Asset>().First();
Assert.That(asset.Documents.Count, Is.EqualTo(2));
Assert.That(asset.DocumentsBag.Count, Is.EqualTo(2));
Assert.That(asset.DocumentsFiltered.Count, Is.EqualTo(1));
}
}
[Test]
public void LazyLoadFiltered()
{
using (var s = OpenSession())
{
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
var asset = s.Query<Asset>().First();
Assert.That(asset.Documents.Count, Is.EqualTo(1));
Assert.That(asset.DocumentsBag.Count, Is.EqualTo(1));
Assert.That(asset.DocumentsFiltered.Count, Is.EqualTo(1));
}
}
} }
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<class name="Base" abstract="true" dynamic-update="true" lazy="true"> <class name="Base" abstract="true" dynamic-update="true" lazy="true">
<id name="Key" column="pdoid" generator="guid.comb"/> <id name="Key" column="pdoid" generator="guid.comb"/>
<property name="IsDeleted" column="IsDeleted" /> <property name="IsDeleted" column="IsDeleted" type="TrueFalse" />
<filter name="deletedFilter" condition="IsDeleted = :deletedParam"/> <filter name="deletedFilter" condition="IsDeleted = :deletedParam"/>
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
</bag> </bag>
<set name="DocumentsFiltered" table="asset_to_document" lazy="true" cascade="none"> <set name="DocumentsFiltered" table="asset_to_document" lazy="true" cascade="none">
<key column="AssetId"/> <key column="AssetId"/>
<many-to-many class="Document" column="DocumentId" where="IsDeleted = 0"/> <many-to-many class="Document" column="DocumentId" where="IsDeleted = 'F'"/>
</set> </set>
</union-subclass> </union-subclass>
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
</union-subclass> </union-subclass>
<filter-def name="deletedFilter"> <filter-def name="deletedFilter">
<filter-param name="deletedParam" type="bool"/> <filter-param name="deletedParam" type="TrueFalse"/>
</filter-def> </filter-def>
</hibernate-mapping> </hibernate-mapping>
...@@ -48,5 +48,21 @@ public void NotIsCorrectlyHandled() ...@@ -48,5 +48,21 @@ public void NotIsCorrectlyHandled()
)"); )");
Assert.That(q.List()[0], Is.EqualTo(0)); Assert.That(q.List()[0], Is.EqualTo(0));
} }
[Test]
public void NotNotExistsNegated()
{
using var log = new SqlLogSpy();
using var session = OpenSession();
var results = session.CreateQuery(
@"SELECT COUNT(ROOT.Id)
FROM Entity AS ROOT
WHERE NOT (
NOT EXISTS (FROM ChildEntity AS CHILD WHERE CHILD.Parent = ROOT)
AND NOT ROOT.Name = 'Parent'
)").List();
Assert.That(log.GetWholeLog(), Does.Not.Contains(" NOT ").IgnoreCase);
Assert.That(results.Count, Is.EqualTo(1));
}
} }
} }
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; }
}
}
using System.Linq;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.GH3352
{
[TestFixture]
public class FetchFromNotMappedBaseClassFixture : 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 void CanFetchLazyComponentFromNotMappedBaseClass()
{
using var session = OpenSession();
var list = session.Query<EntityComponentMapped>().Fetch(x => x.Component).ToList();
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 void CanFetchLazyComponentThenEntityFromNotMappedBaseClass()
{
using var session = OpenSession();
var list = session.Query<EntityComponentMapped>()
.Fetch(x => x.Component)
.ThenFetch(x => x.Entity)
.ThenFetch(x => x.Name)
.ToList();
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 void CanFetchLazyPropertyFromNotMappedBaseClass()
{
using var session = OpenSession();
var list = session.Query<EntityNameMapped>().Fetch(x => x.Name).ToList();
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 void CanThenFetchLazyComponentFromNotMappedBaseClass()
{
using var session = OpenSession();
var list = session.Query<EntityParentMapped>().Fetch(x => x.Parent).ThenFetch(x => x.Component).ToList();
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 void FetchAfterSelect()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var list = s.Query<EntityParentMapped>()
.Select(x => x.Parent)
.Fetch(x => x.Component)
.ThenFetch(x => x.Entity)
.ThenFetch(x => x.Name)
.ToList();
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 void CanFetchEntityFromNotMappedBaseClass()
{
using var session = OpenSession();
var list = session.Query<EntityParentMapped>().Fetch(x => x.Parent).ToList();
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 FetchNotMappedAssociationThrows()
{
using var session = OpenSession();
var query = session.Query<EntityNameMapped>().Fetch(x => x.Parent);
Assert.Throws<QueryException>(() => query.ToList());
}
}
}
...@@ -6,6 +6,11 @@ namespace NHibernate.Test.NHSpecificTest.NH750 ...@@ -6,6 +6,11 @@ namespace NHibernate.Test.NHSpecificTest.NH750
{ {
public class Device public class Device
{ {
private int _id;
private Device _template;
private IList<Drive> _drives = new List<Drive>();
private IList<Drive> _drivesNotIgnored = new List<Drive>();
public Device() : base() public Device() : base()
{ {
} }
...@@ -16,9 +21,13 @@ public Device(string manifacturer) ...@@ -16,9 +21,13 @@ public Device(string manifacturer)
_manifacturer = manifacturer; _manifacturer = manifacturer;
} }
private int _id; public virtual Device Template
{
get => _template;
set => _template = value;
}
public int Id public virtual int Id
{ {
get { return _id; } get { return _id; }
set { _id = value; } set { _id = value; }
...@@ -26,18 +35,23 @@ public int Id ...@@ -26,18 +35,23 @@ public int Id
private string _manifacturer; private string _manifacturer;
public string Manifacturer public virtual string Manifacturer
{ {
get { return _manifacturer; } get { return _manifacturer; }
set { _manifacturer = value; } set { _manifacturer = value; }
} }
private IList<Drive> _drives = new List<Drive>();
public IList<Drive> Drives public virtual IList<Drive> Drives
{ {
get { return _drives; } get { return _drives; }
set { _drives = value; } set { _drives = value; }
} }
public virtual IList<Drive> DrivesNotIgnored
{
get => _drivesNotIgnored;
set => _drivesNotIgnored = value;
}
} }
} }
\ No newline at end of file
...@@ -16,7 +16,7 @@ public Drive(string classFullName) ...@@ -16,7 +16,7 @@ public Drive(string classFullName)
private int _id; private int _id;
public int Id public virtual int Id
{ {
get { return _id; } get { return _id; }
set { _id = value; } set { _id = value; }
...@@ -24,7 +24,7 @@ public int Id ...@@ -24,7 +24,7 @@ public int Id
private string _classFullName; private string _classFullName;
public string ClassFullName public virtual string ClassFullName
{ {
get { return _classFullName; } get { return _classFullName; }
set { _classFullName = value; } set { _classFullName = value; }
...@@ -44,4 +44,4 @@ public override int GetHashCode() ...@@ -44,4 +44,4 @@ public override int GetHashCode()
return _classFullName.GetHashCode(); return _classFullName.GetHashCode();
} }
} }
} }
\ No newline at end of file
using System;
using NHibernate.Cfg;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
[TestFixture]
public class Fixture : BugTestCase
{
protected override void OnTearDown()
{
using (ISession s = Sfi.OpenSession())
{
s.Delete("from Device");
s.Delete("from Drive");
s.Flush();
}
}
protected override void Configure(Configuration configuration)
{
configuration.SetProperty(Cfg.Environment.UseSecondLevelCache, "false");
base.Configure(configuration);
}
[Test]
public void DeviceOfDrive()
{
int[] dvSavedId = new int[2];
Drive dr1 = new Drive("Drive 1");
Drive dr2 = new Drive("Drive 2");
Drive dr3 = new Drive("Drive 3");
Device dv1 = new Device("Device 1");
Device dv2 = new Device("Device 2");
using (ISession s = Sfi.OpenSession())
{
s.Save(dr1);
s.Save(dr2);
s.Save(dr3);
dvSavedId[0] = (int) s.Save(dv1);
dvSavedId[1] = (int) s.Save(dv2);
s.Flush();
}
dv1.Drives.Add(dr1);
dv1.Drives.Add(dr2);
dv2.Drives.Add(dr1);
dv2.Drives.Add(dr3);
using (ISession s = Sfi.OpenSession())
{
dvSavedId[0] = (int) s.Save(dv1);
dvSavedId[1] = (int) s.Save(dv2);
s.Flush();
}
dv1 = null;
dv2 = null;
using (ISession s = Sfi.OpenSession())
{
s.Delete(dr3);
s.Flush();
dv1 = (Device) s.Load(typeof(Device), dvSavedId[0]);
dv2 = (Device) s.Load(typeof(Device), dvSavedId[1]);
}
Assert.AreEqual(2, dv1.Drives.Count);
// Verify one is missing
Assert.AreEqual(1, dv2.Drives.Count);
// Verify dv1 unchanged
Assert.IsTrue(dv1.Drives.Contains(dr1));
Assert.IsTrue(dv1.Drives.Contains(dr2));
// Verify dv2
Assert.IsTrue(dv2.Drives.Contains(dr1));
Assert.IsFalse(dv2.Drives.Contains(dr3));
//Make sure that flush didn't touch not-found="ignore" records for not modified collection
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = s.Get<Device>(dv2.Id);
s.Flush();
t.Commit();
}
VerifyResult(expectedInCollection: 1, expectedInDb: 2, msg: "not modified collection");
//Many-to-many clears collection and recreates it so not-found ignore records are lost
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = s.Get<Device>(dv2.Id);
dv2.Drives.Add(dr2);
t.Commit();
}
VerifyResult(2, 2, msg: "modified collection");
void VerifyResult(int expectedInCollection, int expectedInDb, string msg)
{
using (var s = Sfi.OpenSession())
{
var realCound = Convert.ToInt32(
s.CreateSQLQuery("select count(*) from DriveOfDevice where DeviceId = :id ")
.SetParameter("id", dv2.Id)
.UniqueResult<object>());
dv2 = s.Get<Device>(dv2.Id);
Assert.That(dv2.Drives.Count, Is.EqualTo(expectedInCollection), msg);
Assert.That(realCound, Is.EqualTo(expectedInDb), msg);
}
}
}
}
}
using System;
using NHibernate.Criterion;
using NHibernate.Transform;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
[TestFixture(0)]
[TestFixture(1)]
[TestFixture(2)]
public class ManyToManyFixture : BugTestCase
{
private int id2;
private readonly int _drivesCount;
private int _withTemplateId;
private int ValidDrivesCount => _drivesCount;
public ManyToManyFixture(int drivesCount)
{
_drivesCount = drivesCount;
}
protected override void OnSetUp()
{
Drive dr1 = new Drive("Drive 1");
Drive dr2 = new Drive("Drive 2");
Drive dr3 = new Drive("Drive 3");
Device dv1 = new Device("Device 1");
Device dv2 = new Device("Device 2");
var withTemplate = new Device("Device With Device 2 template") { Template = dv2 };
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.Save(dr1);
s.Save(dr2);
s.Save(dr3);
AddDrive(dv1, dr2);
AddDrive(dv1, dr1);
AddDrive(dv2, dr3);
AddDrive(dv2, dr1);
s.Save(dv1);
id2 = (int) s.Save(dv2);
_withTemplateId = (int)s.Save(withTemplate);
t.Commit();
}
private void AddDrive(Device dv, Drive drive)
{
if(dv.DrivesNotIgnored.Count >= _drivesCount)
return;
dv.DrivesNotIgnored.Add(drive);
}
protected override void OnTearDown()
{
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.CreateSQLQuery("delete from DriveOfDevice").ExecuteUpdate();
s.Delete("from Device");
s.Delete("from Drive");
t.Commit();
}
[Test]
public void QueryOverFetch()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.DrivesNotIgnored)
.Where(Restrictions.IdEq(id2))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefault();
Assert.That(NHibernateUtil.IsInitialized(dv2.DrivesNotIgnored), Is.True);
Assert.That(dv2.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void QueryOverFetch2()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.Template, x => x.Template.DrivesNotIgnored)
.Where(Restrictions.IdEq(_withTemplateId))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefault();
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.DrivesNotIgnored), Is.True);
Assert.That(withTemplate.Template.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void HqlFetch()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = s.CreateQuery("from Device d left join fetch d.DrivesNotIgnored where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResult<Device>();
Assert.That(NHibernateUtil.IsInitialized(dv2.DrivesNotIgnored), Is.True);
Assert.That(dv2.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void HqlFetch2()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = s.CreateQuery("from Device t left join fetch t.Template d left join fetch d.DrivesNotIgnored where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResult<Device>();
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.DrivesNotIgnored), Is.True);
Assert.That(withTemplate.Template.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void LazyLoad()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = s.Get<Device>(id2);
NHibernateUtil.Initialize(dv2.DrivesNotIgnored);
Assert.That(NHibernateUtil.IsInitialized(dv2.DrivesNotIgnored), Is.True);
Assert.That(dv2.DrivesNotIgnored, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
// First query for Device, second for Drives collection
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(2));
}
}
}
using System;
using NHibernate.Criterion;
using NHibernate.Transform;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
[TestFixture(0)]
[TestFixture(1)]
[TestFixture(2)]
public class ManyToManyNotFoundIgnoreFixture : BugTestCase
{
private int id1;
private int id2;
private int _drive2Id;
private int _withTemplateId;
private readonly int _drivesCount;
private int ValidDrivesCount => _drivesCount == 0 ? 0 : _drivesCount - 1;
public ManyToManyNotFoundIgnoreFixture(int drivesCount)
{
_drivesCount = drivesCount;
}
protected override void OnSetUp()
{
Drive dr1 = new Drive("Drive 1");
Drive dr2 = new Drive("Drive 2");
Drive dr3 = new Drive("Drive 3");
Device dv1 = new Device("Device 1");
Device dv2 = new Device("Device 2");
var withTemplate = new Device("Device With Device 2 template") { Template = dv2 };
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.Save(dr1);
_drive2Id = (int)s.Save(dr2);
s.Save(dr3);
AddDrive(dv1, dr2);
AddDrive(dv1, dr1);
AddDrive(dv2, dr3);
AddDrive(dv2, dr1);
id1 = (int) s.Save(dv1);
id2 = (int) s.Save(dv2);
_withTemplateId = (int)s.Save(withTemplate);
s.Flush();
s.Clear();
s.Delete(dr3);
t.Commit();
}
private void AddDrive(Device dv, Drive drive)
{
if(dv.Drives.Count >= _drivesCount)
return;
dv.Drives.Add(drive);
}
protected override void OnTearDown()
{
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
s.CreateSQLQuery("delete from DriveOfDevice").ExecuteUpdate();
s.Delete("from Device");
s.Delete("from Drive");
t.Commit();
}
[Test]
public void DeviceOfDrive()
{
Device dv1;
Device dv2;
using (ISession s = Sfi.OpenSession())
{
dv1 = (Device) s.Load(typeof(Device), id1);
dv2 = (Device) s.Load(typeof(Device), id2);
NHibernateUtil.Initialize(dv1.Drives);
NHibernateUtil.Initialize(dv2.Drives);
}
Assert.That(dv1.Drives, Has.Count.EqualTo(_drivesCount).And.None.Null);
// Verify one is missing
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
//Make sure that flush didn't touch not-found="ignore" records for not modified collection
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = s.Get<Device>(dv2.Id);
s.Flush();
t.Commit();
}
VerifyResult(expectedInCollection: ValidDrivesCount, expectedInDb: _drivesCount, msg: "not modified collection");
// Many-to-many clears collection and recreates it so not-found ignore records are lost
// Note: It's not the case when no valid records are present, so loaded Drives collection is empty
// Just skip this check in this case:
if (_drivesCount < 2)
return;
using (var s = Sfi.OpenSession())
using (var t = s.BeginTransaction())
{
dv2 = s.Get<Device>(dv2.Id);
dv2.Drives.Add(s.Load<Drive>(_drive2Id));
t.Commit();
}
VerifyResult(_drivesCount, _drivesCount, msg: "modified collection");
void VerifyResult(int expectedInCollection, int expectedInDb, string msg)
{
using (var s = Sfi.OpenSession())
{
var realCound = Convert.ToInt32(
s.CreateSQLQuery("select count(*) from DriveOfDevice where DeviceId = :id ")
.SetParameter("id", dv2.Id)
.UniqueResult<object>());
dv2 = s.Get<Device>(dv2.Id);
Assert.That(dv2.Drives.Count, Is.EqualTo(expectedInCollection), msg);
Assert.That(realCound, Is.EqualTo(expectedInDb), msg);
}
}
}
[Test]
public void QueryOverFetch()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.Drives)
.Where(Restrictions.IdEq(id2))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefault();
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void QueryOverFetch2()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.Template, x => x.Template.Drives)
.Where(Restrictions.IdEq(_withTemplateId))
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefault();
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.Drives), Is.True);
Assert.That(withTemplate.Template.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void HqlFetch()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var dv2 = s.CreateQuery("from Device d left join fetch d.Drives where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResult<Device>();
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void HqlFetch2()
{
using var log = new SqlLogSpy();
using var s = OpenSession();
var withTemplate = s.CreateQuery("from Device t left join fetch t.Template d left join fetch d.Drives where d.id = :id")
.SetResultTransformer(Transformers.DistinctRootEntity)
.SetParameter("id", id2)
.UniqueResult<Device>();
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template), Is.True);
Assert.That(NHibernateUtil.IsInitialized(withTemplate.Template.Drives), Is.True);
Assert.That(withTemplate.Template.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
[Test]
public void LazyLoad()
{
using var s = OpenSession();
var dv2 = s.Get<Device>(id2);
using var log = new SqlLogSpy();
Assert.That(dv2.Drives, Has.Count.EqualTo(ValidDrivesCount).And.None.Null);
Assert.That(log.Appender.GetEvents().Length, Is.EqualTo(1));
}
}
}
using System.Linq;
using NHibernate.Criterion;
using NHibernate.Linq;
using NHibernate.Transform;
using NUnit.Framework;
namespace NHibernate.Test.NHSpecificTest.NH750
{
[TestFixture]
public class ManyToManyThrowsForNotFoundFixture : BugTestCase
{
private int _id;
private int _withTemplateId;
protected override void OnSetUp()
{
using var s = Sfi.OpenSession();
using var t = s.BeginTransaction();
Device dv = new Device("Device");
Drive dr = new Drive("Drive");
var withTemplate = new Device("Device With Device 2 template") { Template = dv };
s.Save(dr);
dv.DrivesNotIgnored.Add(dr);
_id = (int) s.Save(dv);
_withTemplateId = (int)s.Save(withTemplate);
s.Flush();
s.Clear();
s.Delete(dr);
t.Commit();
}
protected override void OnTearDown()
{
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
s.Delete("from Device");
s.Delete("from Drive");
t.Commit();
}
}
[Test]
public void LazyLoad()
{
using var s = OpenSession();
var device = s.Get<Device>(_id);
Assert.Throws<ObjectNotFoundException>(() => NHibernateUtil.Initialize(device.DrivesNotIgnored));
}
[Test]
public void QueryOverFetch()
{
using var s = OpenSession();
var queryOver = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x => x.DrivesNotIgnored)
.Where(Restrictions.IdEq(_id))
.TransformUsing(Transformers.DistinctRootEntity);
Assert.Throws<ObjectNotFoundException>(() => queryOver.SingleOrDefault());
}
[Test]
public void QueryOverFetch2()
{
using var s = OpenSession();
var queryOver = s.QueryOver<Device>()
.Fetch(SelectMode.Fetch, x=> x.Template, x => x.Template.DrivesNotIgnored)
.Where(Restrictions.IdEq(_withTemplateId))
.TransformUsing(Transformers.DistinctRootEntity);
Assert.Throws<ObjectNotFoundException>(() => queryOver.SingleOrDefault());
}
[Test]
public void LinqFetch()
{
using var s = OpenSession();
var query = s.Query<Device>()
.Fetch(x => x.DrivesNotIgnored)
.Where(x => x.Id == _id);
Assert.Throws<ObjectNotFoundException>(() => NHibernateUtil.Initialize(query.SingleOrDefault()));
}
[Test]
public void LinqFetch2()
{
using var s = OpenSession();
var query = s.Query<Device>()
.Fetch(x => x.Template)
.ThenFetchMany(x => x.DrivesNotIgnored)
.Where(x => x.Id == _withTemplateId);
Assert.Throws<ObjectNotFoundException>(() => NHibernateUtil.Initialize(query.SingleOrDefault()));
}
}
}
...@@ -2,17 +2,21 @@ ...@@ -2,17 +2,21 @@
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernate.Test" assembly="NHibernate.Test"
namespace="NHibernate.Test.NHSpecificTest.NH750" namespace="NHibernate.Test.NHSpecificTest.NH750"
default-access="field.camelcase-underscore" default-access="field.camelcase-underscore">
default-lazy="false">
<class name="Device"> <class name="Device">
<id name="_id" column="`_id`" type="Int32" access="field"> <id name="Id" column="`_id`" type="Int32">
<generator class="native"/> <generator class="native"/>
</id> </id>
<property name="Manifacturer"/> <property name="Manifacturer"/>
<many-to-one name="Template" />
<bag name="Drives" table="DriveOfDevice" cascade="all"> <bag name="Drives" table="DriveOfDevice" cascade="all">
<key column="DeviceId"/> <key column="DeviceId"/>
<many-to-many class="Drive" column="DriveId" not-found="ignore"/> <many-to-many class="Drive" column="DriveId" not-found="ignore"/>
</bag> </bag>
<bag name="DrivesNotIgnored" lazy="true" table="DriveOfDeviceNotIgnored" cascade="persist" >
<key column="DeviceId"/>
<many-to-many class="Drive" column="DriveId" foreign-key="none"/>
</bag>
</class> </class>
<class name="Drive"> <class name="Drive">
......
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
<Compile Remove="**\NHSpecificTest\NH3121\**" /> <Compile Remove="**\NHSpecificTest\NH3121\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\NHibernate\Hql\Ast\ANTLR\CaseInsensitiveStringStream.cs">
<Link>Hql\Parser\CaseInsensitiveStringStream.cs</Link>
</Compile>
<Compile Include="..\NHibernate\Util\AsyncReaderWriterLock.cs"> <Compile Include="..\NHibernate\Util\AsyncReaderWriterLock.cs">
<Link>UtilityTest\AsyncReaderWriterLock.cs</Link> <Link>UtilityTest\AsyncReaderWriterLock.cs</Link>
</Compile> </Compile>
......
using System.Linq;
using NHibernate.Criterion; using NHibernate.Criterion;
using NHibernate.Linq;
using NUnit.Framework; using NUnit.Framework;
namespace NHibernate.Test.PropertyRef namespace NHibernate.Test.PropertyRef
{ {
[TestFixture] [TestFixture(Description = "NH-2180 (GH-1214)")]
public class ManyToManyPropertyRefFixture : TestCase public class ManyToManyPropertyRefFixture : TestCase
{ {
protected override string[] Mappings => new[] { "PropertyRef.ManyToManyWithPropertyRef.hbm.xml" }; protected override string[] Mappings => new[] { "PropertyRef.ManyToManyWithPropertyRef.hbm.xml" };
protected override string MappingsAssembly => "NHibernate.Test"; protected override string MappingsAssembly => "NHibernate.Test";
private object _manyAId; private long _manyAId;
protected override void OnSetUp() protected override void OnSetUp()
{ {
...@@ -23,7 +25,7 @@ protected override void OnSetUp() ...@@ -23,7 +25,7 @@ protected override void OnSetUp()
var manyB2 = new ManyB { Number = 8, Value = "a value of b2" }; var manyB2 = new ManyB { Number = 8, Value = "a value of b2" };
var manyB3 = new ManyB { Number = 12, Value = "a value of b3" }; var manyB3 = new ManyB { Number = 12, Value = "a value of b3" };
_manyAId = session.Save(manyA); _manyAId = (long) session.Save(manyA);
session.Save(manyB1); session.Save(manyB1);
session.Save(manyB2); session.Save(manyB2);
session.Save(manyB3); session.Save(manyB3);
...@@ -133,5 +135,20 @@ bei System.ThrowHelper.ThrowKeyNotFoundException() ...@@ -133,5 +135,20 @@ bei System.ThrowHelper.ThrowKeyNotFoundException()
Assert.That(loadedManyA.ManyBs, Has.Count.EqualTo(3).And.None.Null); Assert.That(loadedManyA.ManyBs, Has.Count.EqualTo(3).And.None.Null);
} }
[Test]
public void LinqFetch()
{
using (var session = OpenSession())
{
var manyA = session
.Query<ManyA>()
.Where(a => a.Id == _manyAId)
.FetchMany(a => a.ManyBs)
.ToList()
.First();
Assert.That(manyA.ManyBs, Has.Count.EqualTo(3).And.None.Null);
}
}
} }
} }
...@@ -109,9 +109,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist ...@@ -109,9 +109,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
var array = (object[]) disassembled; var array = (object[]) disassembled;
var size = array.Length; var size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
for (var i = 0; i < size; i++) for (var i = 0; i < size; i++)
{ {
var element = await (persister.ElementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false); var element = await (elementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
if (element != null) if (element != null)
{ {
_gbag.Add((T) element); _gbag.Add((T) element);
......
...@@ -44,10 +44,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist ...@@ -44,10 +44,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
object[] array = (object[])disassembled; object[] array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var identifierType = persister.IdentifierType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
await (identifierType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
}
for (int i = 0; i < size; i += 2) for (int i = 0; i < size; i += 2)
{ {
_identifiers[i / 2] = await (persister.IdentifierType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false); _identifiers[i / 2] = await (identifierType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
_values.Add((T) await (persister.ElementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false)); _values.Add((T) await (elementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false));
} }
} }
......
...@@ -98,9 +98,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist ...@@ -98,9 +98,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
object[] array = (object[])disassembled; object[] array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var elementType = persister.ElementType;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
var element = await (persister.ElementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false); await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
for (int i = 0; i < size; i++)
{
var element = await (elementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
WrappedList.Add((T) (element ?? DefaultForType)); WrappedList.Add((T) (element ?? DefaultForType));
} }
} }
......
...@@ -94,10 +94,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist ...@@ -94,10 +94,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
object[] array = (object[])disassembled; object[] array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var indexType = persister.IndexType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
await (indexType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
}
for (int i = 0; i < size; i += 2) for (int i = 0; i < size; i += 2)
{ {
WrappedMap[(TKey)await (persister.IndexType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false)] = WrappedMap[(TKey)await (indexType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false)] =
(TValue)await (persister.ElementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false); (TValue)await (elementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false);
} }
} }
......
...@@ -84,9 +84,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist ...@@ -84,9 +84,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
var array = (object[])disassembled; var array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
var element = await (persister.ElementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false); var element = await (elementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
if (element != null) if (element != null)
{ {
WrappedSet.Add((T) element); WrappedSet.Add((T) element);
......
...@@ -94,9 +94,15 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist ...@@ -94,9 +94,15 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
array = System.Array.CreateInstance(persister.ElementClass, cached.Length); array = System.Array.CreateInstance(persister.ElementClass, cached.Length);
var elementType = persister.ElementType;
for (int i = 0; i < cached.Length; i++) for (int i = 0; i < cached.Length; i++)
{ {
array.SetValue(await (persister.ElementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i); await (elementType.BeforeAssembleAsync(cached[i], Session, cancellationToken)).ConfigureAwait(false);
}
for (int i = 0; i < cached.Length; i++)
{
array.SetValue(await (elementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
} }
} }
......
...@@ -400,9 +400,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object ...@@ -400,9 +400,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object
var array = (object[]) disassembled; var array = (object[]) disassembled;
var size = array.Length; var size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
elementType.BeforeAssemble(array[i], Session);
}
for (var i = 0; i < size; i++) for (var i = 0; i < size; i++)
{ {
var element = persister.ElementType.Assemble(array[i], Session, owner); var element = elementType.Assemble(array[i], Session, owner);
if (element != null) if (element != null)
{ {
_gbag.Add((T) element); _gbag.Add((T) element);
......
...@@ -75,10 +75,19 @@ public override void InitializeFromCache(ICollectionPersister persister, object ...@@ -75,10 +75,19 @@ public override void InitializeFromCache(ICollectionPersister persister, object
object[] array = (object[])disassembled; object[] array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var identifierType = persister.IdentifierType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
identifierType.BeforeAssemble(array[i], Session);
elementType.BeforeAssemble(array[i + 1], Session);
}
for (int i = 0; i < size; i += 2) for (int i = 0; i < size; i += 2)
{ {
_identifiers[i / 2] = persister.IdentifierType.Assemble(array[i], Session, owner); _identifiers[i / 2] = identifierType.Assemble(array[i], Session, owner);
_values.Add((T) persister.ElementType.Assemble(array[i + 1], Session, owner)); _values.Add((T) elementType.Assemble(array[i + 1], Session, owner));
} }
} }
......
...@@ -160,9 +160,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object ...@@ -160,9 +160,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object
object[] array = (object[])disassembled; object[] array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var elementType = persister.ElementType;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
var element = persister.ElementType.Assemble(array[i], Session, owner); elementType.BeforeAssemble(array[i], Session);
}
for (int i = 0; i < size; i++)
{
var element = elementType.Assemble(array[i], Session, owner);
WrappedList.Add((T) (element ?? DefaultForType)); WrappedList.Add((T) (element ?? DefaultForType));
} }
} }
......
...@@ -163,10 +163,19 @@ public override void InitializeFromCache(ICollectionPersister persister, object ...@@ -163,10 +163,19 @@ public override void InitializeFromCache(ICollectionPersister persister, object
object[] array = (object[])disassembled; object[] array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var indexType = persister.IndexType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
indexType.BeforeAssemble(array[i], Session);
elementType.BeforeAssemble(array[i + 1], Session);
}
for (int i = 0; i < size; i += 2) for (int i = 0; i < size; i += 2)
{ {
WrappedMap[(TKey)persister.IndexType.Assemble(array[i], Session, owner)] = WrappedMap[(TKey)indexType.Assemble(array[i], Session, owner)] =
(TValue)persister.ElementType.Assemble(array[i + 1], Session, owner); (TValue)elementType.Assemble(array[i + 1], Session, owner);
} }
} }
...@@ -246,8 +255,9 @@ public void Add(TKey key, TValue value) ...@@ -246,8 +255,9 @@ public void Add(TKey key, TValue value)
{ {
if (key == null) if (key == null)
{ {
throw new ArgumentNullException("key"); throw new ArgumentNullException(nameof(key));
} }
if (PutQueueEnabled) if (PutQueueEnabled)
{ {
var found = TryReadElementByKey<TKey, TValue>(key, out _, out _); var found = TryReadElementByKey<TKey, TValue>(key, out _, out _);
......
...@@ -154,9 +154,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object ...@@ -154,9 +154,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object
var array = (object[])disassembled; var array = (object[])disassembled;
int size = array.Length; int size = array.Length;
BeforeInitialize(persister, size); BeforeInitialize(persister, size);
var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
elementType.BeforeAssemble(array[i], Session);
}
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
var element = persister.ElementType.Assemble(array[i], Session, owner); var element = elementType.Assemble(array[i], Session, owner);
if (element != null) if (element != null)
{ {
WrappedSet.Add((T) element); WrappedSet.Add((T) element);
......
...@@ -196,9 +196,15 @@ public override void InitializeFromCache(ICollectionPersister persister, object ...@@ -196,9 +196,15 @@ public override void InitializeFromCache(ICollectionPersister persister, object
array = System.Array.CreateInstance(persister.ElementClass, cached.Length); array = System.Array.CreateInstance(persister.ElementClass, cached.Length);
var elementType = persister.ElementType;
for (int i = 0; i < cached.Length; i++) for (int i = 0; i < cached.Length; i++)
{ {
array.SetValue(persister.ElementType.Assemble(cached[i], Session, owner), i); elementType.BeforeAssemble(cached[i], Session);
}
for (int i = 0; i < cached.Length; i++)
{
array.SetValue(elementType.Assemble(cached[i], Session, owner), i);
} }
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
using NHibernate.Persister.Collection; using NHibernate.Persister.Collection;
using NHibernate.Persister.Entity; using NHibernate.Persister.Entity;
using NHibernate.SqlCommand; using NHibernate.SqlCommand;
using NHibernate.Type;
namespace NHibernate.Engine namespace NHibernate.Engine
{ {
...@@ -68,14 +69,17 @@ private static bool NeedsTableGroupJoin(IReadOnlyList<IJoin> joins, SqlString[] ...@@ -68,14 +69,17 @@ private static bool NeedsTableGroupJoin(IReadOnlyList<IJoin> joins, SqlString[]
foreach (var join in joins) foreach (var join in joins)
{ {
var entityPersister = GetEntityPersister(join.Joinable, out var isManyToMany); var entityPersister = GetEntityPersister(join.Joinable, out var manyToManyType);
if (manyToManyType?.IsNullable == true)
return true;
if (entityPersister?.HasSubclassJoins(includeSubclasses && isSubclassIncluded(join.Alias)) != true) if (entityPersister?.HasSubclassJoins(includeSubclasses && isSubclassIncluded(join.Alias)) != true)
continue; continue;
if (hasWithClause) if (hasWithClause)
return true; return true;
if (!isManyToMany // many-to-many keys are stored in separate table if (manyToManyType == null // many-to-many keys are stored in separate table
&& entityPersister.ColumnsDependOnSubclassJoins(join.RHSColumns)) && entityPersister.ColumnsDependOnSubclassJoins(join.RHSColumns))
{ {
return true; return true;
...@@ -94,14 +98,14 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm ...@@ -94,14 +98,14 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm
var isAssociationJoin = lhsColumns.Length > 0; var isAssociationJoin = lhsColumns.Length > 0;
if (isAssociationJoin) if (isAssociationJoin)
{ {
var entityPersister = GetEntityPersister(first.Joinable, out var isManyToMany); var entityPersister = GetEntityPersister(first.Joinable, out var manyToManyType);
string rhsAlias = first.Alias; string rhsAlias = first.Alias;
string[] rhsColumns = first.RHSColumns; string[] rhsColumns = first.RHSColumns;
for (int j = 0; j < lhsColumns.Length; j++) for (int j = 0; j < lhsColumns.Length; j++)
{ {
fromFragment.Add(lhsColumns[j]) fromFragment.Add(lhsColumns[j])
.Add("=") .Add("=")
.Add((entityPersister == null || isManyToMany) // many-to-many keys are stored in separate table .Add((entityPersister == null || manyToManyType != null) // many-to-many keys are stored in separate table
? rhsAlias ? rhsAlias
: entityPersister.GenerateTableAliasForColumn(rhsAlias, rhsColumns[j])) : entityPersister.GenerateTableAliasForColumn(rhsAlias, rhsColumns[j]))
.Add(".") .Add(".")
...@@ -116,15 +120,18 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm ...@@ -116,15 +120,18 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm
return fromFragment.ToSqlString(); return fromFragment.ToSqlString();
} }
private static AbstractEntityPersister GetEntityPersister(IJoinable joinable, out bool isManyToMany) private static AbstractEntityPersister GetEntityPersister(IJoinable joinable, out EntityType manyToManyType)
{ {
isManyToMany = false; manyToManyType = null;
if (!joinable.IsCollection) if (!joinable.IsCollection)
return joinable as AbstractEntityPersister; return joinable as AbstractEntityPersister;
var collection = (IQueryableCollection) joinable; var collection = (IQueryableCollection) joinable;
isManyToMany = collection.IsManyToMany; if (!collection.ElementType.IsEntityType)
return collection.ElementType.IsEntityType ? collection.ElementPersister as AbstractEntityPersister : null; return null;
if (collection.IsManyToMany)
manyToManyType = (EntityType) collection.ElementType;
return collection.ElementPersister as AbstractEntityPersister;
} }
private static void AppendWithClause(SqlStringBuilder fromFragment, bool hasConditions, SqlString[] withClauseFragments) private static void AppendWithClause(SqlStringBuilder fromFragment, bool hasConditions, SqlString[] withClauseFragments)
......
...@@ -695,10 +695,9 @@ constant ...@@ -695,10 +695,9 @@ constant
path path
@init { @init {
// TODO - need to clean up DotIdent - suspect that DotIdent2 supersedes the other one, but need to do the analysis HandleDotIdents();
//HandleDotIdent2();
} }
: identifier ( DOT^ { WeakKeywords(); } identifier )* : identifier ( DOT^ identifier )*
; ;
// Wraps the IDENT token from the lexer, in order to provide // Wraps the IDENT token from the lexer, in order to provide
......
...@@ -112,6 +112,27 @@ protected override object RecoverFromMismatchedToken(IIntStream input, int ttype ...@@ -112,6 +112,27 @@ protected override object RecoverFromMismatchedToken(IIntStream input, int ttype
throw new MismatchedTokenException(ttype, input); throw new MismatchedTokenException(ttype, input);
} }
public void HandleDotIdents()
{
int i = 2;
while (input.LA(i) == DOT)
{
var next = input.LT(i + 1);
if (next != null)
next.Type = IDENT;
i += 2;
}
if (input.LA(1) == IDENT || input.LA(2) != DOT)
return;
if (IsPossibleId(input.LT(1)))
{
input.LT(1).Type = IDENT;
}
}
public void WeakKeywords() public void WeakKeywords()
{ {
int t = input.LA(1); int t = input.LA(1);
...@@ -279,10 +300,8 @@ public IASTNode NegateNode(IASTNode node) ...@@ -279,10 +300,8 @@ public IASTNode NegateNode(IASTNode node)
node.Type = BETWEEN; node.Type = BETWEEN;
node.Text = "{not}" + node.Text; node.Text = "{not}" + node.Text;
return node; // (NOT (NOT_BETWEEN a b) ) => (BETWEEN a b) return node; // (NOT (NOT_BETWEEN a b) ) => (BETWEEN a b)
/* This can never happen because this rule will always eliminate the child NOT. case NOT:
case NOT: return node.GetChild(0); // (NOT (NOT x) ) => (x)
return x.getFirstChild(); // (NOT (NOT x) ) => (x)
*/
default: default:
IASTNode not = (IASTNode) TreeAdaptor.Create(NOT, "not"); IASTNode not = (IASTNode) TreeAdaptor.Create(NOT, "not");
not.AddChild(node); not.AddChild(node);
......
...@@ -547,10 +547,6 @@ public IASTNode Parse() ...@@ -547,10 +547,6 @@ public IASTNode Parse()
try try
{ {
var ast = (IASTNode) parser.statement().Tree; var ast = (IASTNode) parser.statement().Tree;
var walker = new NodeTraverser(new ConstantConverter(_sfi));
walker.TraverseDepthFirst(ast);
return ast; return ast;
} }
finally finally
...@@ -558,53 +554,6 @@ public IASTNode Parse() ...@@ -558,53 +554,6 @@ public IASTNode Parse()
parser.ParseErrorHandler.ThrowQueryException(); parser.ParseErrorHandler.ThrowQueryException();
} }
} }
class ConstantConverter : IVisitationStrategy
{
private IASTNode _dotRoot;
private readonly ISessionFactoryImplementor _sfi;
public ConstantConverter(ISessionFactoryImplementor sfi)
{
_sfi = sfi;
}
public void Visit(IASTNode node)
{
if (_dotRoot != null)
{
// we are already processing a dot-structure
if (ASTUtil.IsSubtreeChild(_dotRoot, node))
{
// ignore it...
return;
}
// we are now at a new tree level
_dotRoot = null;
}
if (_dotRoot == null && node.Type == HqlSqlWalker.DOT)
{
_dotRoot = node;
HandleDotStructure(_dotRoot);
}
}
private void HandleDotStructure(IASTNode dotStructureRoot)
{
var expression = ASTUtil.GetPathText(dotStructureRoot);
var constant = ReflectHelper.GetConstantValue(expression, _sfi);
if (constant != null)
{
dotStructureRoot.ClearChildren();
dotStructureRoot.Type = HqlSqlWalker.JAVA_CONSTANT;
dotStructureRoot.Text = expression;
}
}
}
} }
internal class HqlSqlTranslator internal class HqlSqlTranslator
......
...@@ -18,7 +18,7 @@ namespace NHibernate.Hql.Ast.ANTLR.Tree ...@@ -18,7 +18,7 @@ namespace NHibernate.Hql.Ast.ANTLR.Tree
/// Ported by: Steve Strong /// Ported by: Steve Strong
/// </summary> /// </summary>
[CLSCompliant(false)] [CLSCompliant(false)]
public class DotNode : FromReferenceNode public class DotNode : FromReferenceNode, IExpectedTypeAwareNode
{ {
private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof(DotNode)); private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof(DotNode));
...@@ -72,6 +72,8 @@ public class DotNode : FromReferenceNode ...@@ -72,6 +72,8 @@ public class DotNode : FromReferenceNode
/// </summary> /// </summary>
private JoinType _joinType = JoinType.InnerJoin; private JoinType _joinType = JoinType.InnerJoin;
private object _constantValue;
public DotNode(IToken token) : base(token) public DotNode(IToken token) : base(token)
{ {
} }
...@@ -287,11 +289,14 @@ private IType GetDataType() ...@@ -287,11 +289,14 @@ private IType GetDataType()
return DataType; return DataType;
} }
public void SetResolvedConstant(string text) public void SetResolvedConstant(string text) => SetResolvedConstant(text, null);
public void SetResolvedConstant(string text, object value)
{ {
_path = text; _path = text;
_dereferenceType = DerefJavaConstant; _dereferenceType = DerefJavaConstant;
IsResolved = true; // Don't resolve the node again. IsResolved = true; // Don't resolve the node again.
_constantValue = value;
} }
private static QueryException BuildIllegalCollectionDereferenceException(string propertyName, IASTNode lhs) private static QueryException BuildIllegalCollectionDereferenceException(string propertyName, IASTNode lhs)
...@@ -772,5 +777,24 @@ public void ResolveSelectExpression() ...@@ -772,5 +777,24 @@ public void ResolveSelectExpression()
lhs = (FromReferenceNode)lhs.GetChild(0); lhs = (FromReferenceNode)lhs.GetChild(0);
} }
} }
public IType ExpectedType
{
get => DataType;
set
{
if (Type != HqlSqlWalker.JAVA_CONSTANT)
return;
DataType = value;
}
}
public override SqlString RenderText(ISessionFactoryImplementor sessionFactory)
{
return Type == HqlSqlWalker.JAVA_CONSTANT
? JavaConstantNode.ResolveToLiteralString(DataType, _constantValue, sessionFactory.Dialect)
: base.RenderText(sessionFactory);
}
} }
} }
...@@ -424,7 +424,9 @@ private FromElement CreateCollectionJoin(JoinSequence collectionJoinSequence, st ...@@ -424,7 +424,9 @@ private FromElement CreateCollectionJoin(JoinSequence collectionJoinSequence, st
// Add the second join, the one that ends in the destination table. // Add the second join, the one that ends in the destination table.
JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin);
joinSequence.AddJoin(sfh.GetElementAssociationType(_collectionType), tableAlias, joinType, secondJoinColumns); // It's safe to always use inner join for many-to-many not-found ignore mapping as it's processed by table group join
var secondJoinType = type.IsNullable ? JoinType.InnerJoin : joinType;
joinSequence.AddJoin(sfh.GetElementAssociationType(_collectionType), tableAlias, secondJoinType, secondJoinColumns);
elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false); elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false);
elem.UseFromFragment = true; elem.UseFromFragment = true;
} }
......
...@@ -38,27 +38,30 @@ public ISessionFactoryImplementor SessionFactory ...@@ -38,27 +38,30 @@ public ISessionFactoryImplementor SessionFactory
set { _factory = value; } set { _factory = value; }
} }
public override SqlString RenderText(ISessionFactoryImplementor sessionFactory) public override SqlString RenderText(ISessionFactoryImplementor sessionFactory)
{ {
ProcessText(); ProcessText();
IType type = _expectedType ?? _heuristicType; IType type = _expectedType ?? _heuristicType;
return new SqlString(ResolveToLiteralString( type )); return ResolveToLiteralString(type);
} }
private string ResolveToLiteralString(IType type) private SqlString ResolveToLiteralString(IType type)
{ {
try return ResolveToLiteralString(type, _constantValue, _factory.Dialect);
{ }
ILiteralType literalType = (ILiteralType)type;
Dialect.Dialect dialect = _factory.Dialect; internal static SqlString ResolveToLiteralString(IType type, object constantValue, Dialect.Dialect dialect)
return literalType.ObjectToSQLString(_constantValue, dialect); {
} try
catch (Exception t) {
{ return new SqlString(((ILiteralType) type).ObjectToSQLString(constantValue, dialect));
throw new QueryException(LiteralProcessor.ErrorCannotFormatLiteral + Text, t); }
} catch (Exception t)
} {
throw new QueryException(LiteralProcessor.ErrorCannotFormatLiteral + constantValue, t);
}
}
private void ProcessText() private void ProcessText()
{ {
......
...@@ -70,7 +70,7 @@ public void LookupConstant(DotNode node) ...@@ -70,7 +70,7 @@ public void LookupConstant(DotNode node)
} }
else else
{ {
Object value = ReflectHelper.GetConstantValue(text); var value = ReflectHelper.GetConstantValue(text, _walker.SessionFactoryHelper.Factory);
if (value == null) if (value == null)
{ {
throw new InvalidPathException("Invalid path: '" + text + "'"); throw new InvalidPathException("Invalid path: '" + text + "'");
...@@ -149,6 +149,7 @@ public void ProcessConstant(SqlNode constant, bool resolveIdent) ...@@ -149,6 +149,7 @@ public void ProcessConstant(SqlNode constant, bool resolveIdent)
if (isIdent && queryable != null) if (isIdent && queryable != null)
{ {
constant.Text = queryable.DiscriminatorSQLValue; constant.Text = queryable.DiscriminatorSQLValue;
constant.DataType = queryable.DiscriminatorType;
} }
// Otherwise, it's a literal. // Otherwise, it's a literal.
else else
...@@ -275,74 +276,9 @@ private void SetConstantValue(DotNode node, string text, object value) ...@@ -275,74 +276,9 @@ private void SetConstantValue(DotNode node, string text, object value)
node.ClearChildren(); // Chop off the rest of the tree. node.ClearChildren(); // Chop off the rest of the tree.
if (value is string) node.Type = HqlSqlWalker.JAVA_CONSTANT;
{ node.DataType = TypeFactory.HeuristicType(value.GetType().Name);
node.Type = HqlSqlWalker.QUOTED_String; node.SetResolvedConstant(text, value);
}
else if (value is char)
{
node.Type = HqlSqlWalker.QUOTED_String;
}
else if (value is byte)
{
node.Type = HqlSqlWalker.NUM_INT;
}
else if (value is short)
{
node.Type = HqlSqlWalker.NUM_INT;
}
else if (value is int)
{
node.Type = HqlSqlWalker.NUM_INT;
}
else if (value is long)
{
node.Type = HqlSqlWalker.NUM_LONG;
}
else if (value is double)
{
node.Type = HqlSqlWalker.NUM_DOUBLE;
}
else if (value is decimal)
{
node.Type = HqlSqlWalker.NUM_DECIMAL;
}
else if (value is float)
{
node.Type = HqlSqlWalker.NUM_FLOAT;
}
else
{
node.Type = HqlSqlWalker.CONSTANT;
}
IType type;
try
{
type = TypeFactory.HeuristicType(value.GetType().Name);
}
catch (MappingException me)
{
throw new QueryException(me);
}
if (type == null)
{
throw new QueryException(LiteralProcessor.ErrorCannotDetermineType + node.Text);
}
try
{
ILiteralType literalType = (ILiteralType)type;
NHibernate.Dialect.Dialect dialect = _walker.SessionFactoryHelper.Factory.Dialect;
node.Text = literalType.ObjectToSQLString(value, dialect);
}
catch (Exception e)
{
throw new QueryException(LiteralProcessor.ErrorCannotFormatLiteral + node.Text, e);
}
node.DataType = type;
node.SetResolvedConstant(text);
} }
interface IDecimalFormatter interface IDecimalFormatter
......
...@@ -127,6 +127,17 @@ public void AddFromClause(HqlTreeNode from) ...@@ -127,6 +127,17 @@ public void AddFromClause(HqlTreeNode from)
_root.NodesPreOrder.OfType<HqlFrom>().First().AddChild(from); _root.NodesPreOrder.OfType<HqlFrom>().First().AddChild(from);
} }
internal HqlTreeNode GetFromNodeByAlias(string alias) =>
_root.NodesPreOrder
.First(x => x.AstNode.Type == HqlSqlWalker.FROM).Children
.First(x => GetNodeAlias(x) == alias);
private static string GetNodeAlias(HqlTreeNode fromNode) =>
fromNode.Children
.Select(x => x.AstNode)
.First(x => x.Type == HqlSqlWalker.ALIAS)
.Text;
internal HqlRange GetFromRangeClause() internal HqlRange GetFromRangeClause()
{ {
return _root.NodesPreOrder.OfType<HqlFrom>().First().Children.OfType<HqlRange>().FirstOrDefault(); return _root.NodesPreOrder.OfType<HqlFrom>().First().Children.OfType<HqlRange>().FirstOrDefault();
......
...@@ -25,10 +25,7 @@ internal Joiner(QueryModel queryModel) ...@@ -25,10 +25,7 @@ internal Joiner(QueryModel queryModel)
{ {
_nameGenerator = new NameGenerator(queryModel); _nameGenerator = new NameGenerator(queryModel);
_queryModel = queryModel; _queryModel = queryModel;
AddJoinMethod = AddJoin;
} }
internal System.Action<QueryModel, NhJoinClause> AddJoinMethod { get; }
public IEnumerable<NhJoinClause> Joins public IEnumerable<NhJoinClause> Joins
{ {
...@@ -42,7 +39,7 @@ public Expression AddJoin(Expression expression, string key) ...@@ -42,7 +39,7 @@ public Expression AddJoin(Expression expression, string key)
if (!_joins.TryGetValue(key, out join)) if (!_joins.TryGetValue(key, out join))
{ {
join = new NhJoinClause(_nameGenerator.GetNewName(), expression.Type, expression); join = new NhJoinClause(_nameGenerator.GetNewName(), expression.Type, expression);
AddJoinMethod(_queryModel, join); AddJoin(_queryModel, join);
_joins.Add(key, join); _joins.Add(key, join);
} }
......
using System; using System;
using System.Linq; using System.Linq;
using NHibernate.Hql.Ast; using NHibernate.Hql.Ast;
using NHibernate.Hql.Ast.ANTLR;
using NHibernate.Persister.Entity;
using NHibernate.Type; using NHibernate.Type;
using Remotion.Linq.EagerFetching; using Remotion.Linq.EagerFetching;
...@@ -20,7 +22,7 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode ...@@ -20,7 +22,7 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode
public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree, string sourceAlias) public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree, string sourceAlias)
{ {
Process(resultOperator, queryModelVisitor, tree, null, sourceAlias); Process(resultOperator, queryModelVisitor, tree, tree.GetFromNodeByAlias(sourceAlias), sourceAlias);
} }
private void Process( private void Process(
...@@ -54,23 +56,25 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode ...@@ -54,23 +56,25 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode
.GetClassMetadata(resultOperator.RelationMember.ReflectedType); .GetClassMetadata(resultOperator.RelationMember.ReflectedType);
if (metadata == null) if (metadata == null)
{ {
var entityName = queryModelVisitor.VisitorParameters.SessionFactory.GetImplementors( foreach (var entityName in queryModelVisitor.VisitorParameters.SessionFactory
resultOperator.RelationMember.ReflectedType.FullName).FirstOrDefault(); .GetImplementors(resultOperator.RelationMember.ReflectedType.FullName))
if (!string.IsNullOrEmpty(entityName))
{ {
metadata = queryModelVisitor.VisitorParameters.SessionFactory.GetClassMetadata(entityName); if (queryModelVisitor.VisitorParameters.SessionFactory.GetClassMetadata(entityName) is IPropertyMapping propertyMapping
&& propertyMapping.TryToType(resultOperator.RelationMember.Name, out propType))
break;
} }
} }
else
propType = metadata?.GetPropertyType(resultOperator.RelationMember.Name); {
propType = metadata.GetPropertyType(resultOperator.RelationMember.Name);
}
} }
if (propType != null && !propType.IsAssociationType) if (propType != null && !propType.IsAssociationType)
{ {
if (currentNode == null) if (currentNode == null)
{ {
currentNode = tree.GetFromRangeClause() throw new InvalidOperationException($"Property {resultOperator.RelationMember.Name} cannot be fetched for this type of query.");
?? throw new InvalidOperationException($"Property {resultOperator.RelationMember.Name} cannot be fetched for this type of query.");
} }
currentNode.AddChild(tree.TreeBuilder.Fetch()); currentNode.AddChild(tree.TreeBuilder.Fetch());
...@@ -81,12 +85,13 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode ...@@ -81,12 +85,13 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode
{ {
if (componentType == null) if (componentType == null)
{ {
componentType = propType as ComponentType; if (!propType.IsComponentType)
if (componentType == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"Property {innerFetch.RelationMember.Name} cannot be fetched from a non component type property {resultOperator.RelationMember.Name}."); $"Property {innerFetch.RelationMember.Name} cannot be fetched from a non component type property {resultOperator.RelationMember.Name}.");
} }
componentType = (ComponentType) propType;
} }
var subTypeIndex = componentType.GetPropertyIndex(innerFetch.RelationMember.Name); var subTypeIndex = componentType.GetPropertyIndex(innerFetch.RelationMember.Name);
......
...@@ -121,7 +121,7 @@ private void InitStatementString(OuterJoinableAssociation rootAssociation, SqlSt ...@@ -121,7 +121,7 @@ private void InitStatementString(OuterJoinableAssociation rootAssociation, SqlSt
Suffixes = BasicLoader.GenerateSuffixes(joins + 1); Suffixes = BasicLoader.GenerateSuffixes(joins + 1);
var suffix = Suffixes[joins]; var suffix = Suffixes[joins];
selectClause = new SqlString(rootAssociation.GetSelectFragment(suffix, null, null) + SelectString(associations)); selectClause = new SqlString(rootAssociation.GetSelectFragment(suffix, null) + SelectString(associations));
} }
JoinFragment ojf = MergeOuterJoins(associations); JoinFragment ojf = MergeOuterJoins(associations);
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NHibernate.Collection; using NHibernate.Collection;
using NHibernate.Engine; using NHibernate.Engine;
...@@ -348,10 +347,7 @@ private void WalkCollectionTree(IQueryableCollection persister, string alias, st ...@@ -348,10 +347,7 @@ private void WalkCollectionTree(IQueryableCollection persister, string alias, st
string[] aliasedLhsColumns = persister.GetElementColumnNames(alias); string[] aliasedLhsColumns = persister.GetElementColumnNames(alias);
string[] lhsColumns = persister.ElementColumnNames; string[] lhsColumns = persister.ElementColumnNames;
// if the current depth is 0, the root thing being loaded is the const bool useInnerJoin = false;
// many-to-many collection itself. Here, it is alright to use
// an inner join...
bool useInnerJoin = _depth == 0;
var joinType = var joinType =
GetJoinType( GetJoinType(
...@@ -365,6 +361,11 @@ private void WalkCollectionTree(IQueryableCollection persister, string alias, st ...@@ -365,6 +361,11 @@ private void WalkCollectionTree(IQueryableCollection persister, string alias, st
_depth - 1, _depth - 1,
null); null);
// It's safe to always use inner join for many-to-many not-found ignore mapping as it's processed by table group join;
// otherwise we need left join for proper not-found exception handling
if (joinType == JoinType.LeftOuterJoin && ((EntityType) type).IsNullable)
joinType = JoinType.InnerJoin;
AddAssociationToJoinTreeIfNecessary( AddAssociationToJoinTreeIfNecessary(
associationType, associationType,
aliasedLhsColumns, aliasedLhsColumns,
...@@ -884,41 +885,51 @@ protected JoinFragment MergeOuterJoins(IList<OuterJoinableAssociation> associati ...@@ -884,41 +885,51 @@ protected JoinFragment MergeOuterJoins(IList<OuterJoinableAssociation> associati
JoinFragment outerjoin = Dialect.CreateOuterJoinFragment(); JoinFragment outerjoin = Dialect.CreateOuterJoinFragment();
var sortedAssociations = GetSortedAssociations(associations); var sortedAssociations = GetSortedAssociations(associations);
OuterJoinableAssociation last = null; for (var index = 0; index < sortedAssociations.Count; index++)
foreach (OuterJoinableAssociation oj in sortedAssociations)
{ {
if (last != null && last.IsManyToManyWith(oj)) OuterJoinableAssociation oj = sortedAssociations[index];
if (oj.IsCollection && oj.Joinable is IQueryableCollection qc && qc.IsManyToMany && index < sortedAssociations.Count - 1)
{ {
oj.AddManyToManyJoin(outerjoin, (IQueryableCollection) last.Joinable); var entityAssociation = sortedAssociations[index + 1];
var f = qc.GetManyToManyFilterFragment(entityAssociation.RHSAlias, enabledFilters);
if (oj.IsManyToManyWith(entityAssociation)
&& TableGroupJoinHelper.ProcessAsTableGroupJoin(
new[] {oj, entityAssociation},
new[] {oj.On, entityAssociation.On, string.IsNullOrEmpty(f) ? SqlString.Empty : new SqlString(f)},
true,
outerjoin,
alias => true,
factory))
{
index++;
continue;
}
} }
else
// NH Different behavior : NH1179 and NH1293
// Apply filters for entity joins and Many-To-One associations
SqlString filter = null;
var enabledFiltersForJoin = oj.ForceFilter ? enabledFilters : enabledFiltersForManyToOne;
if (oj.ForceFilter || enabledFiltersForJoin.Count > 0)
{ {
// NH Different behavior : NH1179 and NH1293 string manyToOneFilterFragment = oj.Joinable.FilterFragment(oj.RHSAlias, enabledFiltersForJoin);
// Apply filters for entity joins and Many-To-One associations bool joinClauseDoesNotContainsFilterAlready =
SqlString filter = null; oj.On?.IndexOfCaseInsensitive(manyToOneFilterFragment) == -1;
var enabledFiltersForJoin = oj.ForceFilter ? enabledFilters : enabledFiltersForManyToOne; if (joinClauseDoesNotContainsFilterAlready)
if (oj.ForceFilter || enabledFiltersForJoin.Count > 0)
{ {
string manyToOneFilterFragment = oj.Joinable.FilterFragment(oj.RHSAlias, enabledFiltersForJoin); filter = new SqlString(manyToOneFilterFragment);
bool joinClauseDoesNotContainsFilterAlready =
oj.On?.IndexOfCaseInsensitive(manyToOneFilterFragment) == -1;
if (joinClauseDoesNotContainsFilterAlready)
{
filter = new SqlString(manyToOneFilterFragment);
}
} }
}
if (TableGroupJoinHelper.ProcessAsTableGroupJoin(new[] {oj}, new[] {oj.On, filter}, true, outerjoin, alias => true, factory)) if (TableGroupJoinHelper.ProcessAsTableGroupJoin(new[] {oj}, new[] {oj.On, filter}, true, outerjoin, alias => true, factory))
continue; continue;
oj.AddJoins(outerjoin); oj.AddJoins(outerjoin);
// Ensure that the join condition is added to the join, not the where clause. // Ensure that the join condition is added to the join, not the where clause.
// Adding the condition to the where clause causes left joins to become inner joins. // Adding the condition to the where clause causes left joins to become inner joins.
if (SqlStringHelper.IsNotEmpty(filter)) if (SqlStringHelper.IsNotEmpty(filter))
outerjoin.AddFromFragmentString(filter); outerjoin.AddFromFragmentString(filter);
}
last = oj;
} }
return outerjoin; return outerjoin;
...@@ -1212,7 +1223,6 @@ public string SelectString(IList<OuterJoinableAssociation> associations) ...@@ -1212,7 +1223,6 @@ public string SelectString(IList<OuterJoinableAssociation> associations)
for (int i = 0; i < associations.Count; i++) for (int i = 0; i < associations.Count; i++)
{ {
OuterJoinableAssociation join = associations[i]; OuterJoinableAssociation join = associations[i];
OuterJoinableAssociation next = (i == associations.Count - 1) ? null : associations[i + 1];
IJoinable joinable = join.Joinable; IJoinable joinable = join.Joinable;
string entitySuffix = (suffixes == null || entityAliasCount >= suffixes.Length) ? null : suffixes[entityAliasCount]; string entitySuffix = (suffixes == null || entityAliasCount >= suffixes.Length) ? null : suffixes[entityAliasCount];
...@@ -1221,7 +1231,7 @@ public string SelectString(IList<OuterJoinableAssociation> associations) ...@@ -1221,7 +1231,7 @@ public string SelectString(IList<OuterJoinableAssociation> associations)
? null ? null
: collectionSuffixes[collectionAliasCount]; : collectionSuffixes[collectionAliasCount];
string selectFragment = join.GetSelectFragment(entitySuffix, collectionSuffix, next); string selectFragment = join.GetSelectFragment(entitySuffix, collectionSuffix);
if (!string.IsNullOrWhiteSpace(selectFragment)) if (!string.IsNullOrWhiteSpace(selectFragment))
{ {
...@@ -1243,7 +1253,7 @@ public string SelectString(IList<OuterJoinableAssociation> associations) ...@@ -1243,7 +1253,7 @@ public string SelectString(IList<OuterJoinableAssociation> associations)
[Obsolete("This method has no more usages and will be removed in a future version")] [Obsolete("This method has no more usages and will be removed in a future version")]
protected static string GetSelectFragment(OuterJoinableAssociation join, string entitySuffix, string collectionSuffix, OuterJoinableAssociation next = null) protected static string GetSelectFragment(OuterJoinableAssociation join, string entitySuffix, string collectionSuffix, OuterJoinableAssociation next = null)
{ {
return join.GetSelectFragment(entitySuffix, collectionSuffix, next); return join.GetSelectFragment(entitySuffix, collectionSuffix);
} }
protected interface IJoinQueueEntry protected interface IJoinQueueEntry
......
...@@ -169,6 +169,8 @@ public bool IsManyToManyWith(OuterJoinableAssociation other) ...@@ -169,6 +169,8 @@ public bool IsManyToManyWith(OuterJoinableAssociation other)
return false; return false;
} }
//Since 5.4
[Obsolete("This method is not used anymore and will be removed in a next major version")]
public void AddManyToManyJoin(JoinFragment outerjoin, IQueryableCollection collection) public void AddManyToManyJoin(JoinFragment outerjoin, IQueryableCollection collection)
{ {
string manyToManyFilter = collection.GetManyToManyFilterFragment(rhsAlias, enabledFilters); string manyToManyFilter = collection.GetManyToManyFilterFragment(rhsAlias, enabledFilters);
...@@ -204,7 +206,7 @@ internal bool ShouldFetchCollectionPersister() ...@@ -204,7 +206,7 @@ internal bool ShouldFetchCollectionPersister()
throw new ArgumentOutOfRangeException(nameof(SelectMode), SelectMode.ToString()); throw new ArgumentOutOfRangeException(nameof(SelectMode), SelectMode.ToString());
} }
internal string GetSelectFragment(string entitySuffix, string collectionSuffix, OuterJoinableAssociation next) internal string GetSelectFragment(string entitySuffix, string collectionSuffix)
{ {
switch (SelectMode) switch (SelectMode)
{ {
...@@ -212,8 +214,8 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix, ...@@ -212,8 +214,8 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix,
case SelectMode.Fetch: case SelectMode.Fetch:
#pragma warning disable 618 #pragma warning disable 618
return Joinable.SelectFragment( return Joinable.SelectFragment(
next?.Joinable, null,
next?.RHSAlias, null,
RHSAlias, RHSAlias,
entitySuffix, entitySuffix,
collectionSuffix, collectionSuffix,
...@@ -224,8 +226,8 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix, ...@@ -224,8 +226,8 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix,
#pragma warning disable 618 #pragma warning disable 618
return ReflectHelper.CastOrThrow<ISupportSelectModeJoinable>(Joinable, "fetch lazy properties") return ReflectHelper.CastOrThrow<ISupportSelectModeJoinable>(Joinable, "fetch lazy properties")
.SelectFragment( .SelectFragment(
next?.Joinable, null,
next?.RHSAlias, null,
RHSAlias, RHSAlias,
entitySuffix, entitySuffix,
collectionSuffix, collectionSuffix,
...@@ -236,8 +238,6 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix, ...@@ -236,8 +238,6 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix,
case SelectMode.FetchLazyPropertyGroup: case SelectMode.FetchLazyPropertyGroup:
return ReflectHelper.CastOrThrow<ISupportLazyPropsJoinable>(Joinable, "fetch lazy property") return ReflectHelper.CastOrThrow<ISupportLazyPropsJoinable>(Joinable, "fetch lazy property")
.SelectFragment( .SelectFragment(
next?.Joinable,
next?.RHSAlias,
RHSAlias, RHSAlias,
collectionSuffix, collectionSuffix,
ShouldFetchCollectionPersister(), ShouldFetchCollectionPersister(),
......
...@@ -1777,8 +1777,15 @@ public object NotFoundObject ...@@ -1777,8 +1777,15 @@ public object NotFoundObject
return SelectFragment(rhs, rhsAlias, lhsAlias, collectionSuffix, includeCollectionColumns, new EntityLoadInfo(entitySuffix) {IncludeLazyProps = true}); return SelectFragment(rhs, rhsAlias, lhsAlias, collectionSuffix, includeCollectionColumns, new EntityLoadInfo(entitySuffix) {IncludeLazyProps = true});
} }
//6.0 TODO: Make abstract // 6.0 TODO: Remove
[Obsolete("Please use overload without rhs and rhsAlias parameters")]
public virtual string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string currentCollectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo) public virtual string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string currentCollectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{
return SelectFragment(lhsAlias, currentCollectionSuffix, includeCollectionColumns, entityInfo);
}
// 6.0 TODO: Make abstract
public virtual string SelectFragment(string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{ {
throw new NotImplementedException("SelectFragment with fetching lazy properties option is not implemented by " + GetType().FullName); throw new NotImplementedException("SelectFragment with fetching lazy properties option is not implemented by " + GetType().FullName);
} }
......
...@@ -262,51 +262,15 @@ protected override int DoUpdateRows(object id, IPersistentCollection collection, ...@@ -262,51 +262,15 @@ protected override int DoUpdateRows(object id, IPersistentCollection collection,
} }
} }
[Obsolete("Please use overload without rhs and rhsAlias parameters")]
public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo) public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{ {
// we need to determine the best way to know that two joinables return SelectFragment(lhsAlias, collectionSuffix, includeCollectionColumns, entityInfo);
// represent a single many-to-many...
if (rhs != null && IsManyToMany && !rhs.IsCollection)
{
IAssociationType elementType = (IAssociationType) ElementType;
if (rhs.Equals(elementType.GetAssociatedJoinable(Factory)))
{
return ManyToManySelectFragment(rhs, rhsAlias, lhsAlias, collectionSuffix, elementType);
}
}
return includeCollectionColumns
? GetSelectFragment(lhsAlias, collectionSuffix).ToSqlStringFragment(false)
: string.Empty;
} }
private string ManyToManySelectFragment( public override string SelectFragment(string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
IJoinable rhs,
string rhsAlias,
string lhsAlias,
string collectionSuffix,
IAssociationType elementType)
{ {
SelectFragment frag = GenerateSelectFragment(lhsAlias, collectionSuffix); return includeCollectionColumns ? GetSelectFragment(lhsAlias, collectionSuffix).ToSqlStringFragment(false) : string.Empty;
// We need to select in the associated entity table instead of taking the collection actual element,
// because filters can be applied to the entity table outer join. In such case, we need to return null
// for filtered-out elements. (It is tempting to switch to an inner join and just use
// SelectFragment(lhsAlias, collectionSuffix) for many-to-many too, but this would hinder the proper
// handling of the not-found feature.)
var elementColumnNames = string.IsNullOrEmpty(elementType.RHSUniqueKeyPropertyName)
? rhs.KeyColumnNames
// rhs is the entity persister, it does not handle being referenced through an unique key by a
// collection and always yield its identifier columns as KeyColumnNames. We need to resolve the
// key columns instead.
// 6.0 TODO: consider breaking again that IJoinable.SelectFragment interface for transmitting
// the OuterJoinableAssociation instead of its Joinable property. This would allow to get the
// adequate columns directly instead of re-computing them.
: ((IPropertyMapping) rhs).ToColumns(elementType.RHSUniqueKeyPropertyName);
frag.AddColumns(rhsAlias, elementColumnNames, elementColumnAliases);
AppendIndexColumns(frag, lhsAlias);
AppendIdentifierColumns(frag, lhsAlias);
return frag.ToSqlStringFragment(false);
} }
/// <summary> /// <summary>
......
...@@ -294,7 +294,13 @@ protected override int DoUpdateRows(object id, IPersistentCollection collection, ...@@ -294,7 +294,13 @@ protected override int DoUpdateRows(object id, IPersistentCollection collection,
} }
} }
[Obsolete("Please use overload without rhs and rhsAlias parameters")]
public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo) public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{
return SelectFragment(lhsAlias, collectionSuffix, includeCollectionColumns, entityInfo);
}
public override string SelectFragment(string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{ {
var buf = new StringBuilder(); var buf = new StringBuilder();
...@@ -317,7 +323,7 @@ public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhs ...@@ -317,7 +323,7 @@ public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhs
{ {
var selectMode = ReflectHelper.CastOrThrow<ISupportLazyPropsJoinable>(ElementPersister, "fetch lazy properties"); var selectMode = ReflectHelper.CastOrThrow<ISupportLazyPropsJoinable>(ElementPersister, "fetch lazy properties");
if (selectMode != null) if (selectMode != null)
return buf.Append(selectMode.SelectFragment(null, null, lhsAlias, null, false, entityInfo)).ToString(); return buf.Append(selectMode.SelectFragment(lhsAlias, null, false, entityInfo)).ToString();
} }
var ojl = (IOuterJoinLoadable)ElementPersister; var ojl = (IOuterJoinLoadable)ElementPersister;
......
...@@ -4461,10 +4461,17 @@ public override string ToString() ...@@ -4461,10 +4461,17 @@ public override string ToString()
IJoinable rhs, string rhsAlias, string lhsAlias, string entitySuffix, string collectionSuffix, IJoinable rhs, string rhsAlias, string lhsAlias, string entitySuffix, string collectionSuffix,
bool includeCollectionColumns, bool includeLazyProperties) bool includeCollectionColumns, bool includeLazyProperties)
{ {
return SelectFragment(rhs, rhsAlias, lhsAlias, collectionSuffix, includeCollectionColumns, new EntityLoadInfo(entitySuffix) {IncludeLazyProps = includeLazyProperties}); return SelectFragment(lhsAlias, collectionSuffix, includeCollectionColumns, new EntityLoadInfo(entitySuffix) {IncludeLazyProps = includeLazyProperties});
} }
//Since v5.5
[Obsolete("Please use overload without rhs and rhsAlias parameters")]
public string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo) public string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{
return SelectFragment(lhsAlias, collectionSuffix, includeCollectionColumns, entityInfo);
}
public string SelectFragment(string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo)
{ {
return GetIdentifierSelectFragment(lhsAlias, entityInfo.EntitySuffix).ToSqlStringFragment(false) + return GetIdentifierSelectFragment(lhsAlias, entityInfo.EntitySuffix).ToSqlStringFragment(false) +
GetPropertiesSelectFragment( GetPropertiesSelectFragment(
......
...@@ -36,6 +36,6 @@ public EntityLoadInfo(string entitySuffix) ...@@ -36,6 +36,6 @@ public EntityLoadInfo(string entitySuffix)
// 6.0 TODO: merge into 'IJoinable'. // 6.0 TODO: merge into 'IJoinable'.
internal interface ISupportLazyPropsJoinable internal interface ISupportLazyPropsJoinable
{ {
string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo); string SelectFragment(string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo);
} }
} }
...@@ -57,10 +57,9 @@ public static TypeInfo CreateProxyType(System.Type baseType) ...@@ -57,10 +57,9 @@ public static TypeInfo CreateProxyType(System.Type baseType)
var assemblyBuilder = ProxyBuilderHelper.DefineDynamicAssembly(AppDomain.CurrentDomain, name); var assemblyBuilder = ProxyBuilderHelper.DefineDynamicAssembly(AppDomain.CurrentDomain, name);
#if NETFX || NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
if (!baseType.IsVisible) if (!baseType.IsVisible)
ProxyBuilderHelper.GenerateInstanceOfIgnoresAccessChecksToAttribute(assemblyBuilder, baseType.Assembly.GetName().Name); ProxyBuilderHelper.GenerateInstanceOfIgnoresAccessChecksToAttribute(assemblyBuilder, baseType.Assembly.GetName().Name);
#endif
var moduleBuilder = ProxyBuilderHelper.DefineDynamicModule(assemblyBuilder, moduleName); var moduleBuilder = ProxyBuilderHelper.DefineDynamicModule(assemblyBuilder, moduleName);
const TypeAttributes typeAttributes = TypeAttributes.AutoClass | TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.BeforeFieldInit; const TypeAttributes typeAttributes = TypeAttributes.AutoClass | TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.BeforeFieldInit;
...@@ -94,11 +93,11 @@ public static TypeInfo CreateProxyType(System.Type baseType) ...@@ -94,11 +93,11 @@ public static TypeInfo CreateProxyType(System.Type baseType)
private static void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo fieldInterceptorField) private static void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo fieldInterceptorField)
{ {
if (ReflectHelper.IsPropertyGet(method)) if (ReflectHelper.IsPropertyGet(method) && method.GetParameters().Length == 0)
{ {
ImplementGet(typeBuilder, method, fieldInterceptorField); ImplementGet(typeBuilder, method, fieldInterceptorField);
} }
else if (ReflectHelper.IsPropertySet(method)) else if (ReflectHelper.IsPropertySet(method) && method.GetParameters().Length == 1)
{ {
ImplementSet(typeBuilder, method, fieldInterceptorField); ImplementSet(typeBuilder, method, fieldInterceptorField);
} }
......
...@@ -820,31 +820,18 @@ private static MethodInfo SafeGetMethod(System.Type type, MethodInfo method, Sys ...@@ -820,31 +820,18 @@ private static MethodInfo SafeGetMethod(System.Type type, MethodInfo method, Sys
return foundMethod; return foundMethod;
} }
internal static object GetConstantValue(string qualifiedName)
{
return GetConstantValue(qualifiedName, null);
}
internal static object GetConstantValue(string qualifiedName, ISessionFactoryImplementor sfi) internal static object GetConstantValue(string qualifiedName, ISessionFactoryImplementor sfi)
{ {
string className = StringHelper.Qualifier(qualifiedName); string className = StringHelper.Qualifier(qualifiedName);
if (!string.IsNullOrEmpty(className)) if (string.IsNullOrEmpty(className))
{ return null;
System.Type t = System.Type.GetType(className);
if (t == null && sfi != null)
{
t = System.Type.GetType(sfi.GetImportedClassName(className));
}
if (t != null) var t = System.Type.GetType(sfi?.GetImportedClassName(className) ?? className);
{
return GetConstantValue(t, StringHelper.Unqualify(qualifiedName));
}
}
return null; return t == null
? null
: GetConstantValue(t, StringHelper.Unqualify(qualifiedName));
} }
// Since v5 // Since v5
......