diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1180/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1180/FixtureByCode.cs new file mode 100644 index 00000000000..baf5bbacdbb --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1180/FixtureByCode.cs @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using NHibernate.Cfg.MappingSchema; +using NHibernate.Criterion; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH1180 +{ + using System.Threading.Tasks; + [KnownBug("NH-3847 (GH-1180)")] + [TestFixture] + public class ByCodeFixtureAsync : 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.Length(10); }); + rc.Property(x => x.Amount, m => { m.Precision(8); m.Scale(2); }); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + 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 StringTypesAsync() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + // data + await (session.SaveAsync(new Entity {Name = "Alpha"})); + await (session.SaveAsync(new Entity {Name = "Beta"})); + await (session.SaveAsync(new Entity {Name = "Gamma"})); + + await (transaction.CommitAsync()); + } + + // whenTrue is constant, whenFalse is property -> works even before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Not( + Restrictions.Like(nameof(Entity.Name), "B%")), + Projections.Constant("other"), + Projections.Property(nameof(Entity.Name))); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = await (tagCriteria.ListAsync()); + + Assert.That(results, Is.EquivalentTo(new[] {"other", "Beta", "other"})); + } + + // whenTrue is property, whenFalse is constant -> fails before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Like(nameof(Entity.Name), "B%"), + Projections.Property(nameof(Entity.Name)), + Projections.Constant("other")); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = await (tagCriteria.ListAsync()); + + Assert.That(results, Is.EquivalentTo(new[] {"other", "Beta", "other"})); + } + } + + [Test] + public async Task DecimalTypesAsync() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + await (session.SaveAsync(new Entity {Amount = 3.14m})); + await (session.SaveAsync(new Entity {Amount = 42.13m})); + await (session.SaveAsync(new Entity {Amount = 17.99m})); + + await (transaction.CommitAsync()); + } + + // whenTrue is constant, whenFalse is property -> works even before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Not( + Restrictions.Ge(nameof(Entity.Amount), 20m)), + Projections.Constant(20m), + Projections.Property(nameof(Entity.Amount))); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = await (tagCriteria.ListAsync()); + + Assert.That(results, Is.EquivalentTo(new[] {20m, 42.13m, 20m})); + } + + // whenTrue is property, whenFalse is constant -> fails before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Ge(nameof(Entity.Amount), 20m), + Projections.Property(nameof(Entity.Amount)), + Projections.Constant(20m)); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = await (tagCriteria.ListAsync()); + + Assert.That(results, Is.EquivalentTo(new[] {20m, 42.13m, 20m})); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH1180/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH1180/Entity.cs new file mode 100644 index 00000000000..bf16619beb8 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH1180/Entity.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH1180 +{ + internal class Entity + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + public virtual decimal Amount { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH1180/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/GH1180/FixtureByCode.cs new file mode 100644 index 00000000000..a7590840459 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH1180/FixtureByCode.cs @@ -0,0 +1,135 @@ +using NHibernate.Cfg.MappingSchema; +using NHibernate.Criterion; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH1180 +{ + [KnownBug("NH-3847 (GH-1180)")] + [TestFixture] + public class ByCodeFixture : 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.Length(10); }); + rc.Property(x => x.Amount, m => { m.Precision(8); m.Scale(2); }); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + 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 StringTypes() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + // data + session.Save(new Entity {Name = "Alpha"}); + session.Save(new Entity {Name = "Beta"}); + session.Save(new Entity {Name = "Gamma"}); + + transaction.Commit(); + } + + // whenTrue is constant, whenFalse is property -> works even before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Not( + Restrictions.Like(nameof(Entity.Name), "B%")), + Projections.Constant("other"), + Projections.Property(nameof(Entity.Name))); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = tagCriteria.List(); + + Assert.That(results, Is.EquivalentTo(new[] {"other", "Beta", "other"})); + } + + // whenTrue is property, whenFalse is constant -> fails before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Like(nameof(Entity.Name), "B%"), + Projections.Property(nameof(Entity.Name)), + Projections.Constant("other")); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = tagCriteria.List(); + + Assert.That(results, Is.EquivalentTo(new[] {"other", "Beta", "other"})); + } + } + + [Test] + public void DecimalTypes() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Save(new Entity {Amount = 3.14m}); + session.Save(new Entity {Amount = 42.13m}); + session.Save(new Entity {Amount = 17.99m}); + + transaction.Commit(); + } + + // whenTrue is constant, whenFalse is property -> works even before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Not( + Restrictions.Ge(nameof(Entity.Amount), 20m)), + Projections.Constant(20m), + Projections.Property(nameof(Entity.Amount))); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = tagCriteria.List(); + + Assert.That(results, Is.EquivalentTo(new[] {20m, 42.13m, 20m})); + } + + // whenTrue is property, whenFalse is constant -> fails before the fix + using (var session = OpenSession()) + { + ICriteria tagCriteria = session.CreateCriteria(typeof(Entity)); + + var conditionalProjection = Projections.Conditional( + Restrictions.Ge(nameof(Entity.Amount), 20m), + Projections.Property(nameof(Entity.Amount)), + Projections.Constant(20m)); + tagCriteria.SetProjection(conditionalProjection); + + // run query + var results = tagCriteria.List(); + + Assert.That(results, Is.EquivalentTo(new[] {20m, 42.13m, 20m})); + } + } + } +} diff --git a/src/NHibernate/Type/NullableType.cs b/src/NHibernate/Type/NullableType.cs index e84b6df48da..883894ee971 100644 --- a/src/NHibernate/Type/NullableType.cs +++ b/src/NHibernate/Type/NullableType.cs @@ -377,6 +377,15 @@ public override int GetHashCode() return (SqlType.GetHashCode() / 2) + (Name.GetHashCode() / 2); } + /// + /// Provides a more descriptive string representation by reporting the properties that are important for equality. + /// Useful in error messages. + /// + public override string ToString() + { + return $"{base.ToString()} (SqlType: {SqlType})"; + } + #endregion } }