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; } + } +}