diff --git a/src/NHibernate.Test/Async/StaticProxyTest/InterfaceHandling/Fixture.cs b/src/NHibernate.Test/Async/StaticProxyTest/InterfaceHandling/Fixture.cs
new file mode 100644
index 00000000000..80cb249dce3
--- /dev/null
+++ b/src/NHibernate.Test/Async/StaticProxyTest/InterfaceHandling/Fixture.cs
@@ -0,0 +1,202 @@
+//------------------------------------------------------------------------------
+//
+// 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;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.StaticProxyTest.InterfaceHandling
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync : TestCaseMappingByCode
+ {
+ private readonly Guid _id = Guid.NewGuid();
+
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+
+ #region Subclass hierarchy
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.UnionSubclass(
+ rc =>
+ {
+ rc.Proxy(typeof(ISubEntityProxy));
+
+ rc.Property(x => x.AnotherName);
+ });
+
+ mapper.UnionSubclass(
+ rc =>
+ {
+ rc.Proxy(typeof(IAnotherEntityProxy));
+
+ rc.Property(x => x.AnotherName);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Table("ClassWithInterfaceLookup");
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ rc.ManyToOne(x => x.EntityLookup, x => x.Class(typeof(EntityClassProxy)));
+ });
+
+ #endregion Subclass hierarchy
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Table("multiInterface");
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Proxy(typeof(IMultiIdProxy));
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ [Test]
+ public async Task ProxyForBaseSubclassCanBeCreatedAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = await (session.LoadAsync(_id));
+ }
+ }
+
+ //Id access via implicit interface should not lead to proxy initialization
+ [Test]
+ public async Task ProxyClassIdAccessByImplicitInterfaceAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = (IEntity) await (session.LoadAsync(_id));
+ CanAccessIEntityId(entity);
+ ThrowOnIEntityNameAccess(entity);
+ Assert.That(entity.Id, Is.EqualTo(_id));
+
+ var multiInterface = await (session.LoadAsync(_id));
+ CanAccessIEntityId(multiInterface);
+ CanAccessIEntity2Id(multiInterface);
+ Assert.That(multiInterface.Id, Is.EqualTo(_id));
+ }
+ }
+
+ [Test]
+ public async Task ProxyClassIdAccessExplicitInterfaceAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = await (session.LoadAsync(_id));
+
+ ThrowOnIEntityIdAccess(entity);
+ Assert.That(entity.Id, Is.EqualTo(_id));
+ }
+ }
+
+ [Test]
+ public async Task ProxyClassIdAccessBothImplicitExplicitInterfacesAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = await (session.LoadAsync(_id));
+
+ //IEntity2 is implicit and should be accessible without proxy initialization
+ CanAccessIEntity2Id(entity);
+ ThrowOnIEntityIdAccess(entity);
+ }
+ }
+
+ [Test]
+ public async Task ProxyInterfaceIdAccessAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = await (session.LoadAsync(_id));
+
+ CanAccessIEntityId(entity);
+ }
+ }
+
+ [KnownBug("GH-2271")]
+ [Test]
+ public async Task ProxyInterfaceIdAccessFromDifferentInterfacesAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = await (session.LoadAsync(_id));
+
+ CanAccessIEntityId(entity);
+ CanAccessIEntity2Id(entity);
+ }
+ }
+
+ private void ThrowOnIEntityNameAccess(IEntity entity)
+ {
+ Assert.That(() => entity.Name, Throws.TypeOf(), "IEntity.Name access should lead to proxy initialization");
+ }
+
+ private void ThrowOnIEntityIdAccess(IEntity entity)
+ {
+ Assert.That(() => entity.Id, Throws.TypeOf(), "IEntity.Id access should lead to proxy initialization");
+ }
+
+ private void CanAccessIEntityId(IEntity entity)
+ {
+ Assert.That(() => entity.Id, Throws.Nothing, "Failed to access proxy IEntity.Id interface");
+ Assert.That(entity.Id, Is.EqualTo(_id));
+ }
+
+ private void CanAccessIEntity2Id(IEntity2 entity)
+ {
+ Assert.That(() => entity.Id, Throws.Nothing, "Failed to access proxy IEntity2.Id interface");
+ Assert.That(entity.Id, Is.EqualTo(_id));
+ }
+ }
+}
diff --git a/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/BaseInterfaces.cs b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/BaseInterfaces.cs
new file mode 100644
index 00000000000..7beade98061
--- /dev/null
+++ b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/BaseInterfaces.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace NHibernate.Test.StaticProxyTest.InterfaceHandling
+{
+ public interface IEntity
+ {
+ Guid Id { get; set; }
+
+ string Name { get; set; }
+ }
+
+ public interface IEntity2
+ {
+ Guid Id { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/Entities.cs b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/Entities.cs
new file mode 100644
index 00000000000..37976a51382
--- /dev/null
+++ b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/Entities.cs
@@ -0,0 +1,82 @@
+using System;
+
+namespace NHibernate.Test.StaticProxyTest.InterfaceHandling
+{
+ public class EntitySimple : IEntity
+ {
+ public virtual Guid Id { get; set; }
+
+ public virtual string Name { get; set; }
+ }
+
+ public class EntityMultiInterfaces : IEntity, IEntity2
+ {
+ public virtual Guid Id { get; set; }
+
+ public virtual string Name { get; set; }
+ }
+
+ public class EntityExplicitInterface : IEntity
+ {
+ private Guid id;
+ private string name;
+
+ public virtual Guid Id { get; set; }
+
+ public virtual string Name { get; set; }
+
+ Guid IEntity.Id
+ {
+ get => id;
+ set => id = value;
+ }
+
+ string IEntity.Name
+ {
+ get => name;
+ set => name = value;
+ }
+ }
+
+ //Proxy contains IEntity.Id and IEntity2.Id
+ public interface IMultiIdProxy : IEntity, IEntity2
+ {
+ }
+
+ public class EntityMultiIdProxy : IMultiIdProxy
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ }
+
+ public class EntityMixExplicitImplicitInterface : IEntity, IEntity2
+ {
+ private Guid id;
+ private string name;
+
+ public virtual Guid Id { get; set; }
+
+ public virtual string Name { get; set; }
+
+ Guid IEntity.Id
+ {
+ get => id;
+ set => id = value;
+ }
+
+ string IEntity.Name
+ {
+ get => name;
+ set => name = value;
+ }
+ }
+
+ public class EntityWithSuperClassInterfaceLookup
+ {
+ public virtual Guid Id { get; set; }
+
+ public virtual string Name { get; set; }
+
+ public virtual IEntity EntityLookup { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/Fixture.cs b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/Fixture.cs
new file mode 100644
index 00000000000..b2e9e36becb
--- /dev/null
+++ b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/Fixture.cs
@@ -0,0 +1,196 @@
+using System;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.StaticProxyTest.InterfaceHandling
+{
+ [TestFixture]
+ public class Fixture : TestCaseMappingByCode
+ {
+ private readonly Guid _id = Guid.NewGuid();
+
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+
+ #region Subclass hierarchy
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.UnionSubclass(
+ rc =>
+ {
+ rc.Proxy(typeof(ISubEntityProxy));
+
+ rc.Property(x => x.AnotherName);
+ });
+
+ mapper.UnionSubclass(
+ rc =>
+ {
+ rc.Proxy(typeof(IAnotherEntityProxy));
+
+ rc.Property(x => x.AnotherName);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Table("ClassWithInterfaceLookup");
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ rc.ManyToOne(x => x.EntityLookup, x => x.Class(typeof(EntityClassProxy)));
+ });
+
+ #endregion Subclass hierarchy
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Table("multiInterface");
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(
+ rc =>
+ {
+ rc.Proxy(typeof(IMultiIdProxy));
+ rc.Id(x => x.Id);
+ rc.Property(x => x.Name);
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ [Test]
+ public void ProxyForBaseSubclassCanBeCreated()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = session.Load(_id);
+ }
+ }
+
+ //Id access via implicit interface should not lead to proxy initialization
+ [Test]
+ public void ProxyClassIdAccessByImplicitInterface()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = (IEntity) session.Load(_id);
+ CanAccessIEntityId(entity);
+ ThrowOnIEntityNameAccess(entity);
+ Assert.That(entity.Id, Is.EqualTo(_id));
+
+ var multiInterface = session.Load(_id);
+ CanAccessIEntityId(multiInterface);
+ CanAccessIEntity2Id(multiInterface);
+ Assert.That(multiInterface.Id, Is.EqualTo(_id));
+ }
+ }
+
+ [Test]
+ public void ProxyClassIdAccessExplicitInterface()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = session.Load(_id);
+
+ ThrowOnIEntityIdAccess(entity);
+ Assert.That(entity.Id, Is.EqualTo(_id));
+ }
+ }
+
+ [Test]
+ public void ProxyClassIdAccessBothImplicitExplicitInterfaces()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = session.Load(_id);
+
+ //IEntity2 is implicit and should be accessible without proxy initialization
+ CanAccessIEntity2Id(entity);
+ ThrowOnIEntityIdAccess(entity);
+ }
+ }
+
+ [Test]
+ public void ProxyInterfaceIdAccess()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = session.Load(_id);
+
+ CanAccessIEntityId(entity);
+ }
+ }
+
+ [KnownBug("GH-2271")]
+ [Test]
+ public void ProxyInterfaceIdAccessFromDifferentInterfaces()
+ {
+ using (var session = OpenSession())
+ {
+ var entity = session.Load(_id);
+
+ CanAccessIEntityId(entity);
+ CanAccessIEntity2Id(entity);
+ }
+ }
+
+ private void ThrowOnIEntityNameAccess(IEntity entity)
+ {
+ Assert.That(() => entity.Name, Throws.TypeOf(), "IEntity.Name access should lead to proxy initialization");
+ }
+
+ private void ThrowOnIEntityIdAccess(IEntity entity)
+ {
+ Assert.That(() => entity.Id, Throws.TypeOf(), "IEntity.Id access should lead to proxy initialization");
+ }
+
+ private void ThrowOnIEntity2IdAccess(IEntity2 entity)
+ {
+ Assert.That(() => entity.Id, Throws.TypeOf(), "IEntityId.Id access should lead to proxy initialization");
+ }
+
+ private void CanAccessIEntityId(IEntity entity)
+ {
+ Assert.That(() => entity.Id, Throws.Nothing, "Failed to access proxy IEntity.Id interface");
+ Assert.That(entity.Id, Is.EqualTo(_id));
+ }
+
+ private void CanAccessIEntity2Id(IEntity2 entity)
+ {
+ Assert.That(() => entity.Id, Throws.Nothing, "Failed to access proxy IEntity2.Id interface");
+ Assert.That(entity.Id, Is.EqualTo(_id));
+ }
+ }
+}
diff --git a/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/SubclassEntities.cs b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/SubclassEntities.cs
new file mode 100644
index 00000000000..5a5c54b9015
--- /dev/null
+++ b/src/NHibernate.Test/StaticProxyTest/InterfaceHandling/SubclassEntities.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace NHibernate.Test.StaticProxyTest.InterfaceHandling
+{
+ public interface ISubEntityProxy : IEntity
+ {
+ string AnotherName { get; set; }
+ }
+
+ public class EntityClassProxy : IEntity
+ {
+ public virtual Guid Id { get; set; }
+
+ public virtual string Name { get; set; }
+ }
+
+ class SubEntityInterfaceProxy : EntityClassProxy, ISubEntityProxy
+ {
+ public virtual string AnotherName { get; set; }
+ }
+
+ public interface IAnotherEntityProxy : IEntity
+ {
+ string AnotherName { get; set; }
+ }
+
+ class AnotherSubEntityInterfaceProxy : EntityClassProxy, IAnotherEntityProxy
+ {
+ public virtual string AnotherName { get; set; }
+ }
+}