diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3352/FetchFromNotMappedBaseClassFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3352/FetchFromNotMappedBaseClassFixture.cs
new file mode 100644
index 00000000000..07b1a219f83
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3352/FetchFromNotMappedBaseClassFixture.cs
@@ -0,0 +1,174 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Linq;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3352
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FetchFromNotMappedBaseClassFixtureAsync : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name, m => m.Lazy(true));
+ });
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.ManyToOne(x => x.Parent, m => m.ForeignKey("none"));
+ });
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Component(x => x.Component);
+ });
+ mapper.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().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()
+ .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().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().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()
+ .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().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().Fetch(x => x.Parent);
+
+ Assert.ThrowsAsync(() => query.ToListAsync());
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3352/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH3352/Entity.cs
new file mode 100644
index 00000000000..dc7800cc977
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3352/Entity.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH3352
+{
+ public class Entity
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ public virtual EntityComponentMapped Parent { get; set; }
+ public virtual Component Component { get; set; }
+ }
+
+ public class EntityNameMapped : Entity
+ {
+ }
+
+ public class EntityParentMapped : Entity
+ {
+ }
+
+ public class EntityComponentMapped : Entity
+ {
+ }
+
+ public class Component
+ {
+ public string Field { get; set; }
+
+ public EntityNameMapped Entity { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3352/FetchFromNotMappedBaseClassFixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3352/FetchFromNotMappedBaseClassFixture.cs
new file mode 100644
index 00000000000..5b66027c963
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3352/FetchFromNotMappedBaseClassFixture.cs
@@ -0,0 +1,163 @@
+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(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name, m => m.Lazy(true));
+ });
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.ManyToOne(x => x.Parent, m => m.ForeignKey("none"));
+ });
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Component(x => x.Component);
+ });
+ mapper.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().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()
+ .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().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().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()
+ .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().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().Fetch(x => x.Parent);
+
+ Assert.Throws(() => query.ToList());
+ }
+ }
+}
diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFetch.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFetch.cs
index 31401f7df81..7c18f9476f4 100644
--- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFetch.cs
+++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFetch.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using NHibernate.Hql.Ast;
+using NHibernate.Persister.Entity;
using NHibernate.Type;
using Remotion.Linq.EagerFetching;
@@ -54,15 +55,18 @@ private void Process(
.GetClassMetadata(resultOperator.RelationMember.ReflectedType);
if (metadata == null)
{
- var entityName = queryModelVisitor.VisitorParameters.SessionFactory.GetImplementors(
- resultOperator.RelationMember.ReflectedType.FullName).FirstOrDefault();
- if (!string.IsNullOrEmpty(entityName))
+ foreach (var entityName in queryModelVisitor.VisitorParameters.SessionFactory
+ .GetImplementors(resultOperator.RelationMember.ReflectedType.FullName))
{
- metadata = queryModelVisitor.VisitorParameters.SessionFactory.GetClassMetadata(entityName);
+ if (queryModelVisitor.VisitorParameters.SessionFactory.GetClassMetadata(entityName) is IPropertyMapping propertyMapping
+ && propertyMapping.TryToType(resultOperator.RelationMember.Name, out propType))
+ break;
}
}
-
- propType = metadata?.GetPropertyType(resultOperator.RelationMember.Name);
+ else
+ {
+ propType = metadata.GetPropertyType(resultOperator.RelationMember.Name);
+ }
}
if (propType != null && !propType.IsAssociationType)