diff --git a/src/NHibernate.Test/Async/Linq/EnumTests.cs b/src/NHibernate.Test/Async/Linq/EnumTests.cs index e08a2c90829..21f23a84c8b 100644 --- a/src/NHibernate.Test/Async/Linq/EnumTests.cs +++ b/src/NHibernate.Test/Async/Linq/EnumTests.cs @@ -8,8 +8,12 @@ //------------------------------------------------------------------------------ +using System; using System.Linq; -using NHibernate.DomainModel.Northwind.Entities; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NHibernate.SqlTypes; +using NHibernate.Type; using NUnit.Framework; using NHibernate.Linq; @@ -17,94 +21,168 @@ namespace NHibernate.Test.Linq { using System.Threading.Tasks; using System.Threading; - [TestFixture] - public class EnumTestsAsync : LinqTestCase + [TestFixture(typeof(EnumType), "0")] + [TestFixture(typeof(EnumStringType), "'Unspecified'")] + [TestFixture(typeof(EnumAnsiStringType), "'Unspecified'")] + public class EnumTestsAsync : TestCaseMappingByCode { - [Test] - public async Task CanQueryOnEnumStoredAsInt32_High_1Async() + private IType _enumType; + private string _unspecifiedValue; + + public EnumTestsAsync(System.Type enumType, string unspecifiedValue) { - await (CanQueryOnEnumStoredAsInt32Async(EnumStoredAsInt32.High, 1)); + _enumType = (IType) Activator.CreateInstance(enumType); + _unspecifiedValue = unspecifiedValue; } - [Test] - public async Task CanQueryOnEnumStoredAsInt32_Unspecified_2Async() + protected override HbmMapping GetMappings() { - await (CanQueryOnEnumStoredAsInt32Async(EnumStoredAsInt32.Unspecified, 2)); + var mapper = new ModelMapper(); + mapper.Class( + rc => + { + rc.Table("EnumEntity"); + rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); + rc.Property(x => x.Name); + rc.Property(x => x.Enum1, m => m.Type(_enumType)); + rc.Property(x => x.NullableEnum1, m => + { + m.Type(_enumType); + m.Formula($"(case when Enum1 = {_unspecifiedValue} then null else Enum1 end)"); + }); + rc.ManyToOne(x => x.Other, m => m.Cascade(Mapping.ByCode.Cascade.All)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); } - public async Task CanQueryOnEnumStoredAsInt32Async(EnumStoredAsInt32 type, int expectedCount, CancellationToken cancellationToken = default(CancellationToken)) + protected override void OnSetUp() { - var query = await ((from user in db.Users - where user.Enum2 == type - select user).ToListAsync(cancellationToken)); + base.OnSetUp(); + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + session.Save(new EnumEntity { Enum1 = TestEnum.Unspecified }); + session.Save(new EnumEntity { Enum1 = TestEnum.Small }); + session.Save(new EnumEntity { Enum1 = TestEnum.Small }); + session.Save(new EnumEntity { Enum1 = TestEnum.Medium }); + session.Save(new EnumEntity { Enum1 = TestEnum.Medium }); + session.Save(new EnumEntity { Enum1 = TestEnum.Medium }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + trans.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } - Assert.AreEqual(expectedCount, query.Count); + [Test] + public async Task CanQueryOnEnum_Large_4Async() + { + await (CanQueryOnEnumAsync(TestEnum.Large, 4)); + } + + [Test] + public async Task CanQueryOnEnum_Medium_3Async() + { + await (CanQueryOnEnumAsync(TestEnum.Medium, 3)); } [Test] - public async Task CanQueryOnEnumStoredAsString_Meduim_2Async() + public async Task CanQueryOnEnum_Small_2Async() { - await (CanQueryOnEnumStoredAsStringAsync(EnumStoredAsString.Medium, 2)); + await (CanQueryOnEnumAsync(TestEnum.Small, 2)); } [Test] - public async Task CanQueryOnEnumStoredAsString_Small_1Async() + public async Task CanQueryOnEnum_Unspecified_1Async() { - await (CanQueryOnEnumStoredAsStringAsync(EnumStoredAsString.Small, 1)); + await (CanQueryOnEnumAsync(TestEnum.Unspecified, 1)); } - public async Task CanQueryOnEnumStoredAsStringAsync(EnumStoredAsString type, int expectedCount, CancellationToken cancellationToken = default(CancellationToken)) + private async Task CanQueryOnEnumAsync(TestEnum type, int expectedCount, CancellationToken cancellationToken = default(CancellationToken)) { - var query = await ((from user in db.Users - where user.Enum1 == type - select user).ToListAsync(cancellationToken)); + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var query = await (session.Query().Where(x => x.Enum1 == type).ToListAsync(cancellationToken)); - Assert.AreEqual(expectedCount, query.Count); + Assert.AreEqual(expectedCount, query.Count); + } } [Test] - public async Task CanQueryWithContainsOnEnumStoredAsString_Small_1Async() + public async Task CanQueryWithContainsOnTestEnum_Small_1Async() { - var values = new[] { EnumStoredAsString.Small, EnumStoredAsString.Medium }; - var query = await (db.Users.Where(x => values.Contains(x.Enum1)).ToListAsync()); - Assert.AreEqual(3, query.Count); + var values = new[] { TestEnum.Small, TestEnum.Medium }; + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var query = await (session.Query().Where(x => values.Contains(x.Enum1)).ToListAsync()); + + Assert.AreEqual(5, query.Count); + } } [Test] public async Task ConditionalNavigationPropertyAsync() { - EnumStoredAsString? type = null; - await (db.Users.Where(o => o.Enum1 == EnumStoredAsString.Large).ToListAsync()); - await (db.Users.Where(o => EnumStoredAsString.Large != o.Enum1).ToListAsync()); - await (db.Users.Where(o => (o.NullableEnum1 ?? EnumStoredAsString.Large) == EnumStoredAsString.Medium).ToListAsync()); - await (db.Users.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == EnumStoredAsString.Medium).ToListAsync()); - - await (db.Users.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) == EnumStoredAsString.Medium).ToListAsync()); - await (db.Users.Where(o => (o.Enum1 != EnumStoredAsString.Large - ? (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) - : EnumStoredAsString.Small) == EnumStoredAsString.Medium).ToListAsync()); - - await (db.Users.Where(o => (o.Enum1 == EnumStoredAsString.Large ? o.Role : o.Role).Name == "test").ToListAsync()); + TestEnum? type = null; + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var entities = session.Query(); + await (entities.Where(o => o.Enum1 == TestEnum.Large).ToListAsync()); + await (entities.Where(o => TestEnum.Large != o.Enum1).ToListAsync()); + await (entities.Where(o => (o.NullableEnum1 ?? TestEnum.Large) == TestEnum.Medium).ToListAsync()); + await (entities.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == TestEnum.Medium).ToListAsync()); + + await (entities.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : TestEnum.Unspecified) == TestEnum.Medium).ToListAsync()); + await (entities.Where(o => (o.Enum1 != TestEnum.Large + ? (o.NullableEnum1.HasValue ? o.Enum1 : TestEnum.Unspecified) + : TestEnum.Small) == TestEnum.Medium).ToListAsync()); + + await (entities.Where(o => (o.Enum1 == TestEnum.Large ? o.Other : o.Other).Name == "test").ToListAsync()); + } } [Test] - public async Task CanQueryComplexExpressionOnEnumStoredAsStringAsync() + public async Task CanQueryComplexExpressionOnTestEnumAsync() { - var type = EnumStoredAsString.Unspecified; - var query = await ((from user in db.Users - where (user.NullableEnum1 == EnumStoredAsString.Large - ? EnumStoredAsString.Medium - : user.NullableEnum1 ?? user.Enum1 - ) == type - select new - { - user, - simple = user.Enum1, - condition = user.Enum1 == EnumStoredAsString.Large ? EnumStoredAsString.Medium : user.Enum1, - coalesce = user.NullableEnum1 ?? EnumStoredAsString.Medium - }).ToListAsync()); - - Assert.That(query.Count, Is.EqualTo(0)); + //TODO: Fix issue on SQLite with type set to TestEnum.Unspecified + TestEnum? type = null; + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var entities = session.Query(); + + var query = await ((from user in entities + where (user.NullableEnum1 == TestEnum.Large + ? TestEnum.Medium + : user.NullableEnum1 ?? user.Enum1 + ) == type + select new + { + user, + simple = user.Enum1, + condition = user.Enum1 == TestEnum.Large ? TestEnum.Medium : user.Enum1, + coalesce = user.NullableEnum1 ?? TestEnum.Medium + }).ToListAsync()); + + Assert.That(query.Count, Is.EqualTo(0)); + } } } } diff --git a/src/NHibernate.Test/Linq/EnumTests.cs b/src/NHibernate.Test/Linq/EnumTests.cs index 7f312de7e42..1b01c1706d7 100644 --- a/src/NHibernate.Test/Linq/EnumTests.cs +++ b/src/NHibernate.Test/Linq/EnumTests.cs @@ -1,97 +1,214 @@ -using System.Linq; -using NHibernate.DomainModel.Northwind.Entities; +using System; +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NHibernate.SqlTypes; +using NHibernate.Type; using NUnit.Framework; namespace NHibernate.Test.Linq { - [TestFixture] - public class EnumTests : LinqTestCase + [TestFixture(typeof(EnumType), "0")] + [TestFixture(typeof(EnumStringType), "'Unspecified'")] + [TestFixture(typeof(EnumAnsiStringType), "'Unspecified'")] + public class EnumTests : TestCaseMappingByCode { - [Test] - public void CanQueryOnEnumStoredAsInt32_High_1() + private IType _enumType; + private string _unspecifiedValue; + + public EnumTests(System.Type enumType, string unspecifiedValue) { - CanQueryOnEnumStoredAsInt32(EnumStoredAsInt32.High, 1); + _enumType = (IType) Activator.CreateInstance(enumType); + _unspecifiedValue = unspecifiedValue; } - [Test] - public void CanQueryOnEnumStoredAsInt32_Unspecified_2() + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class( + rc => + { + rc.Table("EnumEntity"); + rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); + rc.Property(x => x.Name); + rc.Property(x => x.Enum1, m => m.Type(_enumType)); + rc.Property(x => x.NullableEnum1, m => + { + m.Type(_enumType); + m.Formula($"(case when Enum1 = {_unspecifiedValue} then null else Enum1 end)"); + }); + rc.ManyToOne(x => x.Other, m => m.Cascade(Mapping.ByCode.Cascade.All)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void OnSetUp() { - CanQueryOnEnumStoredAsInt32(EnumStoredAsInt32.Unspecified, 2); + base.OnSetUp(); + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + session.Save(new EnumEntity { Enum1 = TestEnum.Unspecified }); + session.Save(new EnumEntity { Enum1 = TestEnum.Small }); + session.Save(new EnumEntity { Enum1 = TestEnum.Small }); + session.Save(new EnumEntity { Enum1 = TestEnum.Medium }); + session.Save(new EnumEntity { Enum1 = TestEnum.Medium }); + session.Save(new EnumEntity { Enum1 = TestEnum.Medium }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + session.Save(new EnumEntity { Enum1 = TestEnum.Large }); + trans.Commit(); + } } - public void CanQueryOnEnumStoredAsInt32(EnumStoredAsInt32 type, int expectedCount) + protected override void OnTearDown() { - var query = (from user in db.Users - where user.Enum2 == type - select user).ToList(); + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); - Assert.AreEqual(expectedCount, query.Count); + session.Flush(); + transaction.Commit(); + } } [Test] - public void CanQueryOnEnumStoredAsString_Meduim_2() + public void CanQueryOnEnum_Large_4() { - CanQueryOnEnumStoredAsString(EnumStoredAsString.Medium, 2); + CanQueryOnEnum(TestEnum.Large, 4); } [Test] - public void CanQueryOnEnumStoredAsString_Small_1() + public void CanQueryOnEnum_Medium_3() { - CanQueryOnEnumStoredAsString(EnumStoredAsString.Small, 1); + CanQueryOnEnum(TestEnum.Medium, 3); } - public void CanQueryOnEnumStoredAsString(EnumStoredAsString type, int expectedCount) + [Test] + public void CanQueryOnEnum_Small_2() { - var query = (from user in db.Users - where user.Enum1 == type - select user).ToList(); + CanQueryOnEnum(TestEnum.Small, 2); + } - Assert.AreEqual(expectedCount, query.Count); + [Test] + public void CanQueryOnEnum_Unspecified_1() + { + CanQueryOnEnum(TestEnum.Unspecified, 1); + } + + private void CanQueryOnEnum(TestEnum type, int expectedCount) + { + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var query = session.Query().Where(x => x.Enum1 == type).ToList(); + + Assert.AreEqual(expectedCount, query.Count); + } } [Test] - public void CanQueryWithContainsOnEnumStoredAsString_Small_1() + public void CanQueryWithContainsOnTestEnum_Small_1() { - var values = new[] { EnumStoredAsString.Small, EnumStoredAsString.Medium }; - var query = db.Users.Where(x => values.Contains(x.Enum1)).ToList(); - Assert.AreEqual(3, query.Count); + var values = new[] { TestEnum.Small, TestEnum.Medium }; + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var query = session.Query().Where(x => values.Contains(x.Enum1)).ToList(); + + Assert.AreEqual(5, query.Count); + } } [Test] public void ConditionalNavigationProperty() { - EnumStoredAsString? type = null; - db.Users.Where(o => o.Enum1 == EnumStoredAsString.Large).ToList(); - db.Users.Where(o => EnumStoredAsString.Large != o.Enum1).ToList(); - db.Users.Where(o => (o.NullableEnum1 ?? EnumStoredAsString.Large) == EnumStoredAsString.Medium).ToList(); - db.Users.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == EnumStoredAsString.Medium).ToList(); - - db.Users.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) == EnumStoredAsString.Medium).ToList(); - db.Users.Where(o => (o.Enum1 != EnumStoredAsString.Large - ? (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) - : EnumStoredAsString.Small) == EnumStoredAsString.Medium).ToList(); - - db.Users.Where(o => (o.Enum1 == EnumStoredAsString.Large ? o.Role : o.Role).Name == "test").ToList(); + TestEnum? type = null; + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var entities = session.Query(); + entities.Where(o => o.Enum1 == TestEnum.Large).ToList(); + entities.Where(o => TestEnum.Large != o.Enum1).ToList(); + entities.Where(o => (o.NullableEnum1 ?? TestEnum.Large) == TestEnum.Medium).ToList(); + entities.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == TestEnum.Medium).ToList(); + + entities.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : TestEnum.Unspecified) == TestEnum.Medium).ToList(); + entities.Where(o => (o.Enum1 != TestEnum.Large + ? (o.NullableEnum1.HasValue ? o.Enum1 : TestEnum.Unspecified) + : TestEnum.Small) == TestEnum.Medium).ToList(); + + entities.Where(o => (o.Enum1 == TestEnum.Large ? o.Other : o.Other).Name == "test").ToList(); + } } [Test] - public void CanQueryComplexExpressionOnEnumStoredAsString() + public void CanQueryComplexExpressionOnTestEnum() + { + //TODO: Fix issue on SQLite with type set to TestEnum.Unspecified + TestEnum? type = null; + using (var session = OpenSession()) + using (var trans = session.BeginTransaction()) + { + var entities = session.Query(); + + var query = (from user in entities + where (user.NullableEnum1 == TestEnum.Large + ? TestEnum.Medium + : user.NullableEnum1 ?? user.Enum1 + ) == type + select new + { + user, + simple = user.Enum1, + condition = user.Enum1 == TestEnum.Large ? TestEnum.Medium : user.Enum1, + coalesce = user.NullableEnum1 ?? TestEnum.Medium + }).ToList(); + + Assert.That(query.Count, Is.EqualTo(0)); + } + } + } + + public class EnumEntity + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + + public virtual TestEnum Enum1 { get; set; } + public virtual TestEnum? NullableEnum1 { get; set; } + + public virtual EnumEntity Other { get; set; } + } + + public enum TestEnum + { + Unspecified, + Small, + Medium, + Large + } + + [Serializable] + public class EnumAnsiStringType : EnumStringType + { + private readonly string typeName; + + public EnumAnsiStringType() + : base(typeof(T)) { - var type = EnumStoredAsString.Unspecified; - var query = (from user in db.Users - where (user.NullableEnum1 == EnumStoredAsString.Large - ? EnumStoredAsString.Medium - : user.NullableEnum1 ?? user.Enum1 - ) == type - select new - { - user, - simple = user.Enum1, - condition = user.Enum1 == EnumStoredAsString.Large ? EnumStoredAsString.Medium : user.Enum1, - coalesce = user.NullableEnum1 ?? EnumStoredAsString.Medium - }).ToList(); - - Assert.That(query.Count, Is.EqualTo(0)); + System.Type type = GetType(); + typeName = type.FullName + ", " + type.Assembly.GetName().Name; } + + public override string Name + { + get { return typeName; } + } + + public override SqlType SqlType => SqlTypeFactory.GetAnsiString(255); } } diff --git a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs index 34315c240d2..35df768b2a0 100644 --- a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs +++ b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs @@ -634,7 +634,7 @@ private bool IsCastRequired(IType type, IType toType, out bool existType) return false; } - if (type.ReturnedClass.IsEnum && sqlTypes[0].DbType == DbType.String) + if (type.ReturnedClass.IsEnum && sqlTypes[0].DbType.IsStringType()) { existType = false; return false; // Never cast an enum that is mapped as string, the type will provide a string for the parameter value diff --git a/src/NHibernate/Util/DbTypeExtensions.cs b/src/NHibernate/Util/DbTypeExtensions.cs new file mode 100644 index 00000000000..5f56a0d56c8 --- /dev/null +++ b/src/NHibernate/Util/DbTypeExtensions.cs @@ -0,0 +1,14 @@ +using System.Data; + +namespace NHibernate.Util +{ + internal static class DbTypeExtensions + { + /// + /// Checks whether the type is a , , or + /// + /// + /// + public static bool IsStringType(this DbType dbType) => dbType == DbType.String || dbType == DbType.AnsiString || dbType == DbType.StringFixedLength || dbType == DbType.AnsiStringFixedLength; + } +}