Skip to content

Commit 7c6e99c

Browse files
Support evaluation of DateTime.Now on db side
And of all similar properties: UtcNow, Today, and DateTimeOffset's ones. Part of #959
1 parent 018cf1c commit 7c6e99c

34 files changed

+771
-30
lines changed

doc/reference/modules/configuration.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,29 @@ var session = sessions.OpenSession(conn);
717717
</para>
718718
</entry>
719719
</row>
720+
<row>
721+
<entry>
722+
<literal>linqtohql.legacy_preevaluation</literal>
723+
</entry>
724+
<entry>
725+
Whether to use the legacy pre-evaluation or not in Linq queries. Defaults to <literal>false</literal>.
726+
<para>
727+
<emphasis role="strong">eg.</emphasis>
728+
<literal>true</literal> | <literal>false</literal>
729+
</para>
730+
<para>
731+
Legacy pre-evaluation is causing special properties or functions like <literal>DateTime.Now</literal>
732+
or <literal>Guid.NewGuid()</literal> to be always evaluated with the .Net runtime and replaced in the
733+
query by parameter values.
734+
</para>
735+
<para>
736+
The new pre-evaluation allows them to be converted to HQL function calls which will be run on the db
737+
side. This allows for example to retrieve the server time instead of the client time, or to generate
738+
UUIDs for each row instead of an unique one for all rows. (This does not happen if the dialect does
739+
not support the required HQL function.)
740+
</para>
741+
</entry>
742+
</row>
720743
<row>
721744
<entry>
722745
<literal>sql_exception_converter</literal>

src/NHibernate.Test/Async/Linq/MiscellaneousTextFixture.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public class MiscellaneousTextFixtureAsync : LinqTestCase
2727
[Test(Description = "This sample uses Count to find the number of Orders placed before yesterday in the database.")]
2828
public async Task CountWithWhereClauseAsync()
2929
{
30-
var q = from o in db.Orders where o.OrderDate <= DateTime.Today.AddDays(-1) select o;
30+
var yesterday = DateTime.Today.AddDays(-1);
31+
var q = from o in db.Orders where o.OrderDate <= yesterday select o;
3132

3233
var count = await (q.CountAsync());
3334

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using System.Collections.Generic;
13+
using System.Linq;
14+
using NHibernate.Cfg;
15+
using NHibernate.SqlTypes;
16+
using NUnit.Framework;
17+
using Environment = NHibernate.Cfg.Environment;
18+
using NHibernate.Linq;
19+
20+
namespace NHibernate.Test.Linq
21+
{
22+
using System.Threading.Tasks;
23+
[TestFixture]
24+
public class PreEvaluationTestsAsync : LinqTestCase
25+
{
26+
protected virtual bool LegacyPreEvaluation => false;
27+
28+
protected override void Configure(Configuration configuration)
29+
{
30+
base.Configure(configuration);
31+
32+
configuration.SetProperty(Environment.FormatSql, "false");
33+
if (LegacyPreEvaluation)
34+
configuration.SetProperty(Environment.LinqToHqlLegacyPreEvaluation, "true");
35+
}
36+
37+
[Test]
38+
public async Task CanQueryByDateTimeNowAsync()
39+
{
40+
using (var spy = new SqlLogSpy())
41+
{
42+
var x = await (db.Orders.CountAsync(o => o.OrderDate.Value < DateTime.Now));
43+
44+
Assert.That(x, Is.GreaterThan(0));
45+
AssertFunctionInSql("current_timestamp", spy);
46+
}
47+
}
48+
49+
[Test]
50+
public async Task CanSelectDateTimeNowAsync()
51+
{
52+
using (var spy = new SqlLogSpy())
53+
{
54+
var x =
55+
await (db
56+
.Orders.Select(o => new { id = o.OrderId, d = DateTime.Now })
57+
.OrderBy(o => o.id).Take(1).ToListAsync());
58+
59+
Assert.That(x, Has.Count.GreaterThan(0));
60+
AssertFunctionInSql("current_timestamp", spy);
61+
}
62+
}
63+
64+
[Test]
65+
public async Task CanQueryByDateTimeUtcNowAsync()
66+
{
67+
using (var spy = new SqlLogSpy())
68+
{
69+
var x = await (db.Orders.CountAsync(o => o.OrderDate.Value < DateTime.UtcNow));
70+
71+
Assert.That(x, Is.GreaterThan(0));
72+
AssertFunctionInSql("current_utctimestamp", spy);
73+
}
74+
}
75+
76+
[Test]
77+
public async Task CanSelectDateTimeUtcNowAsync()
78+
{
79+
using (var spy = new SqlLogSpy())
80+
{
81+
var x =
82+
await (db
83+
.Orders.Select(o => new { id = o.OrderId, d = DateTime.UtcNow })
84+
.OrderBy(o => o.id).Take(1).ToListAsync());
85+
86+
Assert.That(x, Has.Count.GreaterThan(0));
87+
AssertFunctionInSql("current_utctimestamp", spy);
88+
}
89+
}
90+
91+
[Test]
92+
public async Task CanQueryByDateTimeTodayAsync()
93+
{
94+
using (var spy = new SqlLogSpy())
95+
{
96+
var x = await (db.Orders.CountAsync(o => o.OrderDate.Value < DateTime.Today));
97+
98+
Assert.That(x, Is.GreaterThan(0));
99+
AssertFunctionInSql("current_date", spy);
100+
}
101+
}
102+
103+
[Test]
104+
public async Task CanSelectDateTimeTodayAsync()
105+
{
106+
using (var spy = new SqlLogSpy())
107+
{
108+
var x =
109+
await (db
110+
.Orders.Select(o => new { id = o.OrderId, d = DateTime.Today })
111+
.OrderBy(o => o.id).Take(1).ToListAsync());
112+
113+
Assert.That(x, Has.Count.GreaterThan(0));
114+
AssertFunctionInSql("current_date", spy);
115+
}
116+
}
117+
118+
[Test]
119+
public async Task CanQueryByDateTimeOffsetTimeNowAsync()
120+
{
121+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
122+
Assert.Ignore("Dialect does not support DateTimeOffSet");
123+
124+
using (var spy = new SqlLogSpy())
125+
{
126+
var testDate = DateTimeOffset.Now.AddDays(-1);
127+
var x = await (db.Orders.CountAsync(o => testDate < DateTimeOffset.Now));
128+
129+
Assert.That(x, Is.GreaterThan(0));
130+
AssertFunctionInSql("current_timestamp_offset", spy);
131+
}
132+
}
133+
134+
[Test]
135+
public async Task CanSelectDateTimeOffsetNowAsync()
136+
{
137+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
138+
Assert.Ignore("Dialect does not support DateTimeOffSet");
139+
140+
using (var spy = new SqlLogSpy())
141+
{
142+
var x =
143+
await (db
144+
.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.Now })
145+
.OrderBy(o => o.id).Take(1).ToListAsync());
146+
147+
Assert.That(x, Has.Count.GreaterThan(0));
148+
AssertFunctionInSql("current_timestamp_offset", spy);
149+
}
150+
}
151+
152+
[Test]
153+
public async Task CanQueryByDateTimeOffsetUtcNowAsync()
154+
{
155+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
156+
Assert.Ignore("Dialect does not support DateTimeOffSet");
157+
158+
using (var spy = new SqlLogSpy())
159+
{
160+
var testDate = DateTimeOffset.UtcNow.AddDays(-1);
161+
var x = await (db.Orders.CountAsync(o => testDate < DateTimeOffset.UtcNow));
162+
163+
Assert.That(x, Is.GreaterThan(0));
164+
AssertFunctionInSql("current_utctimestamp_offset", spy);
165+
}
166+
}
167+
168+
[Test]
169+
public async Task CanSelectDateTimeOffsetUtcNowAsync()
170+
{
171+
if (!TestDialect.SupportsSqlType(SqlTypeFactory.DateTimeOffSet))
172+
Assert.Ignore("Dialect does not support DateTimeOffSet");
173+
174+
using (var spy = new SqlLogSpy())
175+
{
176+
var x =
177+
await (db
178+
.Orders.Select(o => new { id = o.OrderId, d = DateTimeOffset.UtcNow })
179+
.OrderBy(o => o.id).Take(1).ToListAsync());
180+
181+
Assert.That(x, Has.Count.GreaterThan(0));
182+
AssertFunctionInSql("current_utctimestamp_offset", spy);
183+
}
184+
}
185+
186+
private void AssertFunctionInSql(string functionName, SqlLogSpy spy)
187+
{
188+
if (!IsFunctionSupported(functionName))
189+
Assert.Inconclusive($"{functionName} is not supported by the dialect");
190+
191+
var function = Dialect.Functions[functionName].Render(new List<object>(), Sfi).ToString();
192+
193+
if (LegacyPreEvaluation)
194+
Assert.That(spy.GetWholeLog(), Does.Not.Contain(function));
195+
else
196+
Assert.That(spy.GetWholeLog(), Does.Contain(function));
197+
}
198+
}
199+
200+
[TestFixture]
201+
public class PreEvaluationLegacyTestsAsync : PreEvaluationTestsAsync
202+
{
203+
protected override bool LegacyPreEvaluation => true;
204+
}
205+
}

src/NHibernate.Test/Linq/MiscellaneousTextFixture.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ from s in db.Shippers
2727
[Test(Description = "This sample uses Count to find the number of Orders placed before yesterday in the database.")]
2828
public void CountWithWhereClause()
2929
{
30-
var q = from o in db.Orders where o.OrderDate <= DateTime.Today.AddDays(-1) select o;
30+
var yesterday = DateTime.Today.AddDays(-1);
31+
var q = from o in db.Orders where o.OrderDate <= yesterday select o;
3132

3233
var count = q.Count();
3334

0 commit comments

Comments
 (0)