Skip to content

Test case that NHibernate starts to compute the boolean value incorrect after a certain scenario #2528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Reflection;
using NHibernate.Hql.Ast;
using NHibernate.Linq.Functions;
using NHibernate.Linq.Visitors;
using NHibernate.Util;

namespace NHibernate.Test.NHSpecificTest.GH2529
{
public class DateTimeAddYearsMethodHqlGenerator : BaseHqlGeneratorForMethod
{
public DateTimeAddYearsMethodHqlGenerator()
{
SupportedMethods = new[] {
ReflectHelper.GetMethodDefinition((DateTime x) => x.AddYears(0))
};
}

public override HqlTreeNode BuildHql(
MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor
)
{
return treeBuilder.MethodCall(
nameof(DateTime.AddYears),
visitor.Visit(targetObject)
.AsExpression(),
visitor.Visit(arguments[0])
.AsExpression()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using NHibernate.Hql.Ast;
using NHibernate.Linq.Functions;
using NHibernate.Linq.Visitors;
using NHibernate.Util;

namespace NHibernate.Test.NHSpecificTest.GH2529
{
public class DateTimeDayOfYearPropertyHqlGenerator : BaseHqlGeneratorForProperty
{
public DateTimeDayOfYearPropertyHqlGenerator()
{
SupportedProperties = new[]
{
ReflectHelper.GetProperty((DateTime x) => x.DayOfYear)
};
}

public override HqlTreeNode BuildHql(
MemberInfo member,
Expression expression,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor
)
{
return treeBuilder.MethodCall(
nameof(DateTime.DayOfYear),
visitor.Visit(expression)
.AsExpression()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using NHibernate.Linq.Functions;

namespace NHibernate.Test.NHSpecificTest.GH2529
{
public class EnhancedLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public EnhancedLinqToHqlGeneratorsRegistry()
{
this.Merge(new DateTimeDayOfYearPropertyHqlGenerator());
this.Merge(new DateTimeAddYearsMethodHqlGenerator());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using NHibernate.Dialect;
using NHibernate.Dialect.Function;

namespace NHibernate.Test.NHSpecificTest.GH2529
{
public class EnhancedMsSql2008Dialect : MsSql2008Dialect
{
protected override void RegisterFunctions()
{
base.RegisterFunctions();

RegisterFunction(
nameof(DateTime.DayOfYear),
new SQLFunctionTemplate(
NHibernateUtil.Int32,
"datepart(dy, ?1)"
)
);

RegisterFunction(
nameof(DateTime.AddYears),
new SQLFunctionTemplate(
NHibernateUtil.DateTime,
"dateadd(year,?2,?1)"
)
);
}
}
}
172 changes: 172 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH2529/FixtureByCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH2529
{
/// <summary>
/// Fixture using 'by code' mappings
/// </summary>
/// <remarks>
/// This fixture is identical to <see cref="Fixture" /> except the <see cref="User" /> mapping is performed
/// by code in the GetMappings method, and does not require the <c>Mappings.hbm.xml</c> file. Use this approach
/// if you prefer.
/// </remarks>
[TestFixture]
public class ByCodeFixture : TestCaseMappingByCode
{
protected override void Configure(Configuration configuration)
{
base.Configure(configuration);

configuration.LinqToHqlGeneratorsRegistry<EnhancedLinqToHqlGeneratorsRegistry>();
configuration.SetProperty(Cfg.Environment.Dialect, typeof(EnhancedMsSql2008Dialect).AssemblyQualifiedName);
}

protected override bool AppliesTo(Dialect.Dialect dialect)
{
return dialect is EnhancedMsSql2008Dialect;
}

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<User>(rc =>
{
rc.Table("Users");
rc.Id(x => x.Id, m => m.Generator(Generators.Identity));
rc.Property(x => x.Birthday);
});

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, Explicit]
public void List_DealsWithYearTransitionProperly()
{
// arrange
DateTimeOffset today = new DateTimeOffset(2001, 12, 27, 08, 00, 0, TimeSpan.Zero);

CreateUser(birthday: new DateTime(1991, 12, 31));
CreateUser(birthday: new DateTime(1992, 01, 01));

// act
IReadOnlyCollection<BirthdayListItem> result = List(
today
);

// arrange
Assert.That(result.Count, Is.EqualTo(2));
BirthdayListItem firstBirthday = result.First();
BirthdayListItem secondBirthday = result.Skip(1).Single();
Assert.That(firstBirthday.IsThisYearsBirthdayInThePastNHibernate, Is.EqualTo(firstBirthday.IsThisYearsBirthdayInThePastManual));
Assert.That(secondBirthday.IsThisYearsBirthdayInThePastNHibernate, Is.EqualTo(secondBirthday.IsThisYearsBirthdayInThePastManual));
}

[Test, Explicit]
public void List_WorksForTomorrow()
{
// arrange
DateTimeOffset today = new DateTime(2000, 05, 11, 00, 00, 0, DateTimeKind.Utc);
CreateUser(birthday: today.AddDays(1).DateTime);

// act
IReadOnlyCollection<BirthdayListItem> result = List(
today
);

// arrange
Assert.That(result.Count, Is.EqualTo(1));
BirthdayListItem firstBirthday = result.First();
Assert.That(firstBirthday.IsThisYearsBirthdayInThePastNHibernate, Is.EqualTo(firstBirthday.IsThisYearsBirthdayInThePastManual));
}

[Test]
public void List_Both()
{
// Running these methods individual passes the test
// Running them in sequence fails on List_WorksForTomorrow
List_DealsWithYearTransitionProperly();
DeleteAllUsers();
List_WorksForTomorrow();
}

private User CreateUser(
DateTime birthday
)
{
using (ISession session = OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
var user = new User
{
Birthday = birthday,
};

session.Save(user);
transaction.Commit();

return user;
}
}

private void DeleteAllUsers()
{
using (ISession session = OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
string queryString = $"DELETE {typeof(User).FullName}";
IQuery query = session.CreateQuery(queryString);
query.ExecuteUpdate();

transaction.Commit();
}
}

private IReadOnlyCollection<BirthdayListItem> List(
DateTimeOffset today
)
{
using (ISession session = OpenSession())
{
return session.Query<User>()
.Where(x => x.Birthday != null)
.Select(x => new
{
BirthdayThisYear = x.Birthday.Value.AddYears(today.Year - x.Birthday.Value.Year),
BirthdayNextYear = x.Birthday.Value.AddYears((today.Year - x.Birthday.Value.Year) + 1),
IsThisYearsBirthdayInThePast = x.Birthday.Value.AddYears(today.Year - x.Birthday.Value.Year).DayOfYear < today.DayOfYear,
})
.OrderBy(x => x.IsThisYearsBirthdayInThePast ? x.BirthdayNextYear : x.BirthdayThisYear)
.ToArray()
.Select(x => new BirthdayListItem
{
IsThisYearsBirthdayInThePastNHibernate = x.IsThisYearsBirthdayInThePast,
IsThisYearsBirthdayInThePastManual = x.BirthdayThisYear.DayOfYear < today.DayOfYear,
})
.ToArray();
}
}

public class BirthdayListItem
{
public bool IsThisYearsBirthdayInThePastNHibernate { get; set; }
public bool IsThisYearsBirthdayInThePastManual { get; set; }
}
}
}
11 changes: 11 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH2529/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace NHibernate.Test.NHSpecificTest.GH2529
{
public class User
{
public virtual int Id { get; set; }

public virtual DateTime? Birthday { get; set; }
}
}
3 changes: 3 additions & 0 deletions src/NHibernate.Test/NHibernate.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,7 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="NHSpecificTest\GH0000\CustomHqlGenerators\" />
</ItemGroup>
</Project>