Skip to content

Commit ac0545f

Browse files
committed
Merge branch '7.2'
# Conflicts: # Directory.Build.props # Orm.sln # Orm/Xtensive.Orm.Tests.FSharp/ArithmeticOperationsCompilersTest.fs # Orm/Xtensive.Orm.Tests.FSharp/ComparisonCompilersTest.fs # Orm/Xtensive.Orm.Tests.FSharp/Model.fs # Orm/Xtensive.Orm.Tests.FSharp/QueryEntityBackTest.fs # Orm/Xtensive.Orm.Tests.FSharp/Xtensive.Orm.Tests.FSharp.fsproj # Orm/Xtensive.Orm.Tests.VB/Xtensive.Orm.Tests.VB.vbproj # Orm/Xtensive.Orm/Linq/ExpressionExtensions.cs # Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs # Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/FSharpConversionsCompilers.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/FSharpMathOperationsCompilers.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/FSharpOperatorsCompilers.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/FSharpStringCompilers.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/VBStringsCompilers.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/VbConversionsCompilers.cs # Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/VbDateAndTimeCompilers.cs # Version.props
2 parents a811f76 + 960c567 commit ac0545f

16 files changed

Lines changed: 249 additions & 61 deletions

File tree

.github/workflows/reusable-storage-dependant-tests.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,20 @@ jobs:
116116
timeout-minutes: 10
117117
run: '${{ vars[env.DOCKER_SCRIPT_KEY] }}'
118118

119-
- name: Build Orm
120-
if: ${{ success() }}
121-
timeout-minutes: 5
122-
run: dotnet build Orm.sln -c ${{ inputs.build_config }}
123-
124119
- name: Setup .NETs
125120
if: ${{ success() }}
126121
timeout-minutes: 2
127122
uses: ./.github/actions/setup-dotnets
128123
with:
129124
target_framework: ${{ env.DO_TargetFrameworks }}
130125

126+
- name: Build Orm
127+
if: ${{ success() }}
128+
timeout-minutes: 5
129+
run: dotnet build Orm.sln -c ${{ inputs.build_config }}
130+
131+
132+
131133
# Since composite action syntax does not allow to define step timeout
132134
# we have to run Reprocessing project here, because it may stuck in forever-loop
133135
- name: Test Reprocessing extension set of tests

.github/workflows/reusable-storage-independant-tests.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,18 @@ jobs:
8484
if: ${{ inputs.specific_sha != '' && github.reg_type == 'branch' }}
8585
run: git checkout ${{ inputs.specific_sha }}
8686

87-
- name: Build Orm
88-
if: ${{ success() }}
89-
timeout-minutes: 5
90-
run: dotnet build Orm.sln -c ${{ inputs.build_config }}
91-
9287
- name: Setup .NETs
9388
if: ${{ success() }}
9489
timeout-minutes: 2
9590
uses: ./.github/actions/setup-dotnets
9691
with:
9792
target_framework: ${{ env.DO_TargetFrameworks }}
9893

94+
- name: Build Orm
95+
if: ${{ success() }}
96+
timeout-minutes: 5
97+
run: dotnet build Orm.sln -c ${{ inputs.build_config }}
98+
9999
- name: Run tests
100100
id: complex_tests
101101
if: ${{ success() }}

ChangeLog/7.2.2-dev.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[main] Query.CreateDelayedQuery(key, Func<IOrderedQueryable<TElement>>) applies external key instead of default computed, as it suppose to
2+
[main] QueryEndpoint.SingleAsync()/SingleOrDefaultAsync() get overloads that can recieve one key value as parameter without need to create array explicitly
3+
[main] PrefetchQuery<T> implements IAsyncEnumerable<T>, extra call for .AsAsyncEmumerable() is not needed for async enumeration
4+
[main] PrefetchQuery<T>.AsAsyncEmumerable() is marked as Obsolete
5+
[main] Support for C#14+ optimization that applies ReadOnlySpan<T>.Contains() extension instead of IEnumerable<T>.Contains() one to arrays
6+
[main] Compatibility with VB and FSharp improved

Orm/Xtensive.Orm.Manual/Prefetch/PrefetchTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public async Task MainAsyncTest()
180180
.Prefetch(p => p.Photo) // Lazy load field
181181
.Prefetch(p => p.Employees.Prefetch(e => e.Photo)) // EntitySet Employees and lazy load field of each of its items with the limit on number of items to be loaded
182182
.Prefetch(p => p.Manager.Photo); // Referenced entity and lazy load field for each of them
183-
await foreach (var person in people.AsAsyncEnumerable()) {
183+
await foreach (var person in people) {
184184
var accessor = DirectStateAccessor.Get(person);
185185
Assert.That(accessor.GetFieldState("Photo"), Is.EqualTo(PersistentFieldState.Loaded));
186186
Assert.That(accessor.GetFieldState("Manager"), Is.EqualTo(PersistentFieldState.Loaded));
@@ -275,7 +275,7 @@ orderby person.Name
275275
.Prefetch(p => p.Employees // EntitySet Employees
276276
.Prefetch(e => e.Photo)) // and lazy load field of each of its items
277277
.Prefetch(p => p.Manager); // Referenced entity
278-
await foreach (var person in prefetchedPeople.AsAsyncEnumerable()) {
278+
await foreach (var person in prefetchedPeople) {
279279
var accessor = DirectStateAccessor.Get(person);
280280
Assert.That(accessor.GetFieldState("Photo"), Is.EqualTo(PersistentFieldState.Loaded));
281281
Assert.That(accessor.GetFieldState("Manager"), Is.EqualTo(PersistentFieldState.Loaded));
@@ -362,7 +362,7 @@ public async Task DelayedQueryAsyncTest()
362362
.Prefetch(p => p.Photo) // Lazy load field
363363
.Prefetch(p => p.Employees.Prefetch(e => e.Photo)) // EntitySet Employees and lazy load field of each of its items with the limit on number of items to be loaded
364364
.Prefetch(p => p.Manager.Photo); // Referenced entity and lazy load field for each of them
365-
await foreach (var person in people.AsAsyncEnumerable()) {
365+
await foreach (var person in people) {
366366
var accessor = DirectStateAccessor.Get(person);
367367
Assert.That(accessor.GetFieldState("Photo"), Is.EqualTo(PersistentFieldState.Loaded));
368368
Assert.That(accessor.GetFieldState("Manager"), Is.EqualTo(PersistentFieldState.Loaded));
@@ -450,7 +450,7 @@ public async Task CachedQueryAsyncTest()
450450
.Prefetch(p => p.Photo) // Lazy load field
451451
.Prefetch(p => p.Employees.Prefetch(e => e.Photo)) // EntitySet Employees and lazy load field of each of its items with the limit on number of items to be loaded
452452
.Prefetch(p => p.Manager.Photo); // Referenced entity and lazy load field for each of them
453-
await foreach (var person in people.AsAsyncEnumerable()) {
453+
await foreach (var person in people) {
454454
var accessor = DirectStateAccessor.Get(person);
455455
Assert.That(accessor.GetFieldState("Photo"), Is.EqualTo(PersistentFieldState.Loaded));
456456
Assert.That(accessor.GetFieldState("Manager"), Is.EqualTo(PersistentFieldState.Loaded));

Orm/Xtensive.Orm.Tests.FSharp/Model.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,4 @@ type X() =
9393
[<Field>]
9494
member this.TimeSpanField
9595
with get() = this.GetFieldValue<System.TimeSpan> (nameof this.TimeSpanField)
96-
and set(v: System.TimeSpan) = this.SetFieldValue (nameof this.TimeSpanField, v)
96+
and set(v: System.TimeSpan) = this.SetFieldValue (nameof this.TimeSpanField, v)

Orm/Xtensive.Orm.Tests.FSharp/QueryEntityBackTest.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ type Fixture() =
3232
let list = query |> Seq.toArray
3333
Assert.That(list.Length, Is.EqualTo(1))
3434
let fetched = list.[0]
35-
Assert.That(fetched.Name, Is.EqualTo("John"))
35+
Assert.That(fetched.Name, Is.EqualTo("John"))
36+

Orm/Xtensive.Orm.Tests.FSharp/Xtensive.Orm.Tests.FSharp.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@
2828
<ProjectReference Include="..\Xtensive.Orm\Xtensive.Orm.csproj" />
2929
</ItemGroup>
3030
<ItemGroup>
31+
<!-- Keep it before any tests -->
3132
<Compile Include="FsUnit.fs" />
3233
<Compile Include="Model.fs" />
34+
</ItemGroup>
35+
<ItemGroup>
3336
<Compile Include="ArithmeticOperationsCompilersTest.fs" />
3437
<Compile Include="ComparisonCompilersTest.fs" />
3538
<Compile Include="DateTimeCompilersTest.fs" />

Orm/Xtensive.Orm.Tests/Storage/Prefetch/FetchByKeyWithCachingTest.cs

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public void SingleByExistingIdTest()
143143
}
144144

145145
[Test]
146-
public async Task SingleByExistingIdAsyncTest()
146+
public async Task SingleByExistingIdAsyncTest1()
147147
{
148148
await RunWithinSessionAsync(async (s) => {
149149
var existingId = (int) existingKeys[customerType][0].Value.GetValue(0, out var _);
@@ -164,6 +164,28 @@ await RunWithinSessionAsync(async (s) => {
164164
});
165165
}
166166

167+
[Test]
168+
public async Task SingleByExistingIdAsyncTest2()
169+
{
170+
await RunWithinSessionAsync(async (s) => {
171+
var existingId = (int) existingKeys[customerType][0].Value.GetValue(0, out var _);
172+
var detector = new QueryExecutionDetector();
173+
using (detector.Attach(s)) {
174+
// Entity is not in cache yet
175+
var existingEntity = await s.Query.SingleAsync<Customer>(existingId);
176+
}
177+
Assert.That(detector.DbCommandsDetected, Is.True);
178+
detector.Reset();
179+
180+
using (detector.Attach(s)) {
181+
// now it is in cache
182+
var existingEntity = await s.Query.SingleAsync<Customer>(existingId);
183+
}
184+
Assert.That(detector.DbCommandsDetected, Is.False);
185+
detector.Reset();
186+
});
187+
}
188+
167189
[Test]
168190
public void SingleByInexistentIdTest()
169191
{
@@ -186,7 +208,7 @@ public void SingleByInexistentIdTest()
186208
}
187209

188210
[Test]
189-
public async Task SingleByInexistentIdAsyncTest()
211+
public async Task SingleByInexistentIdAsyncTest1()
190212
{
191213
await RunWithinSessionAsync(async (s) => {
192214
var inexistentId = 9999;
@@ -207,6 +229,28 @@ await RunWithinSessionAsync(async (s) => {
207229
});
208230
}
209231

232+
[Test]
233+
public async Task SingleByInexistentIdAsyncTest2()
234+
{
235+
await RunWithinSessionAsync(async (s) => {
236+
var inexistentId = 9999;
237+
238+
var detector = new QueryExecutionDetector();
239+
using (detector.Attach(s)) {
240+
_ = Assert.ThrowsAsync<KeyNotFoundException>(async () => await s.Query.SingleAsync<Customer>(inexistentId));
241+
}
242+
Assert.That(detector.DbCommandsDetected, Is.True);
243+
detector.Reset();
244+
245+
using (detector.Attach(s)) {
246+
_ = Assert.ThrowsAsync<KeyNotFoundException>(async () => await s.Query.SingleAsync<Customer>(inexistentId));
247+
}
248+
Assert.That(detector.DbCommandsDetected, Is.False);
249+
detector.Reset();
250+
await Task.CompletedTask;
251+
});
252+
}
253+
210254
[Test]
211255
public void SingleOrDefaultByExistingKeyTest()
212256
{
@@ -327,7 +371,7 @@ public void SingleOrDefaultByExistingIdTest()
327371
}
328372

329373
[Test]
330-
public async Task SingleOrDefaultByExistingIdAsyncTest()
374+
public async Task SingleOrDefaultByExistingIdAsyncTest1()
331375
{
332376
await RunWithinSessionAsync(async (s) => {
333377
var existingId = (int) existingKeys[customerType][0].Value.GetValue(0, out var _);
@@ -348,6 +392,28 @@ await RunWithinSessionAsync(async (s) => {
348392
});
349393
}
350394

395+
[Test]
396+
public async Task SingleOrDefaultByExistingIdAsyncTest2()
397+
{
398+
await RunWithinSessionAsync(async (s) => {
399+
var existingId = (int) existingKeys[customerType][0].Value.GetValue(0, out var _);
400+
var detector = new QueryExecutionDetector();
401+
using (detector.Attach(s)) {
402+
// Entity is not in cache yet
403+
var existingEntity = await s.Query.SingleOrDefaultAsync<Customer>(existingId);
404+
}
405+
Assert.That(detector.DbCommandsDetected, Is.True);
406+
detector.Reset();
407+
408+
using (detector.Attach(s)) {
409+
// now it is in cache
410+
var existingEntity = await s.Query.SingleOrDefaultAsync<Customer>(existingId);
411+
}
412+
Assert.That(detector.DbCommandsDetected, Is.False);
413+
detector.Reset();
414+
});
415+
}
416+
351417
[Test]
352418
public void SingleOrDefaultByInexistentIdTest()
353419
{
@@ -372,7 +438,7 @@ public void SingleOrDefaultByInexistentIdTest()
372438
}
373439

374440
[Test]
375-
public async Task SingleOrDefaultByInexistentIdAsyncTest()
441+
public async Task SingleOrDefaultByInexistentIdAsyncTest1()
376442
{
377443
await RunWithinSessionAsync(async (s) => {
378444
var inexistentId = 9999;
@@ -395,6 +461,29 @@ await RunWithinSessionAsync(async (s) => {
395461
});
396462
}
397463

464+
[Test]
465+
public async Task SingleOrDefaultByInexistentIdAsyncTest2()
466+
{
467+
await RunWithinSessionAsync(async (s) => {
468+
var inexistentId = 9999;
469+
470+
var detector = new QueryExecutionDetector();
471+
using (detector.Attach(s)) {
472+
var shouldBeNull = await s.Query.SingleOrDefaultAsync<Customer>(inexistentId);
473+
Assert.That(shouldBeNull, Is.Null);
474+
}
475+
Assert.That(detector.DbCommandsDetected, Is.True);
476+
detector.Reset();
477+
478+
using (detector.Attach(s)) {
479+
var shouldBeNull = await s.Query.SingleOrDefaultAsync<Customer>(inexistentId);
480+
Assert.That(shouldBeNull, Is.Null);
481+
}
482+
Assert.That(detector.DbCommandsDetected, Is.False);
483+
detector.Reset();
484+
await Task.CompletedTask;
485+
});
486+
}
398487

399488
private void RunWithinSession(Action<Session> testAction)
400489
{

Orm/Xtensive.Orm.Tests/Storage/Prefetch/PrefetchTest.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ public async Task PrefetchGraphPerformanceAsyncTest()
181181
.Prefetch(i => i.InvoiceLines
182182
.Prefetch(id => id.Track)
183183
.Prefetch(id => id.Track.Album)
184-
.Prefetch(id => id.Track.Bytes))
185-
.AsAsyncEnumerable();
184+
.Prefetch(id => id.Track.Bytes));
186185
await foreach (var invoice in invoices) {
187186
var id = invoice.InvoiceId;
188187
var name = invoice.Customer.CompanyName;
@@ -247,7 +246,7 @@ public async Task EnumerableOfNonEntityAsyncTest()
247246
await using (var session = await Domain.OpenSessionAsync())
248247
await using (var tx = session.OpenTransaction()) {
249248
var invoices = session.Query.Many<Invoice>(keys)
250-
.Prefetch(o => o.DesignatedEmployee).AsAsyncEnumerable();
249+
.Prefetch(o => o.DesignatedEmployee);
251250
var invoiceType = Domain.Model.Types[typeof(Invoice)];
252251
var employeeField = invoiceType.Fields[nameof(Invoice.DesignatedEmployee)];
253252
var employeeType = Domain.Model.Types[typeof(Employee)];
@@ -361,7 +360,7 @@ public async Task PrefetchManyNotFullBatchAsyncTest()
361360
var invoicesField = Domain.Model.Types[typeof (Track)].Fields["Playlists"];
362361
var invoiceLinesField = Domain.Model.Types[typeof (Playlist)].Fields["Tracks"];
363362
var tracks = session.Query.All<Track>().Take(50)
364-
.Prefetch(t => t.Playlists.Prefetch(il => il.Tracks)).AsAsyncEnumerable();
363+
.Prefetch(t => t.Playlists.Prefetch(il => il.Tracks));
365364
int count1 = 0, count2 = 0;
366365
await foreach (var track in tracks) {
367366
count1++;
@@ -413,7 +412,7 @@ public async Task PrefetchManySeveralBatchesAsyncTest()
413412
var trackField = Domain.Model.Types[typeof(InvoiceLine)].Fields[nameof(InvoiceLine.Track)];
414413
var invoices = session.Query.All<Invoice>()
415414
.Take(90)
416-
.Prefetch(o => o.InvoiceLines.Prefetch(od => od.Track)).AsAsyncEnumerable();
415+
.Prefetch(o => o.InvoiceLines.Prefetch(od => od.Track));
417416
int count1 = 0, count2 = 0;
418417
await foreach (var invoice in invoices) {
419418
count1++;
@@ -483,7 +482,7 @@ public async Task PrefetchSingleAsyncTest()
483482
var employeeType = Domain.Model.Types[typeof(Employee)];
484483
var invoiceType = Domain.Model.Types[typeof(Invoice)];
485484
var invoices = session.Query.Many<Invoice>(keys)
486-
.Prefetch(o => o.DesignatedEmployee.Invoices).AsAsyncEnumerable();
485+
.Prefetch(o => o.DesignatedEmployee.Invoices);
487486
var count = 0;
488487
await foreach (var invoice in invoices) {
489488
Assert.That(invoice.Key, Is.EqualTo(keys[count]));
@@ -674,7 +673,7 @@ public async Task SimultaneouslyUsageOfMultipleEnumeratorsAsyncTest()
674673
await using (var session = await Domain.OpenSessionAsync())
675674
await using (var tx = session.OpenTransaction()) {
676675
var source = session.Query.All<Invoice>().ToList();
677-
var prefetchQuery = source.Prefetch(i => i.InvoiceLines).AsAsyncEnumerable();
676+
var prefetchQuery = source.Prefetch(i => i.InvoiceLines);
678677
await using (var enumerator0 = prefetchQuery.GetAsyncEnumerator()) {
679678
_ = await enumerator0.MoveNextAsync();
680679
_ = await enumerator0.MoveNextAsync();
@@ -729,7 +728,7 @@ public async Task RootElementIsNullPrefetchAsyncTest()
729728

730729
await using (var tx = session.OpenTransaction()) {
731730
var books = session.Query.All<PrefetchModel.Book>().AsEnumerable()
732-
.Concat(Enumerable.Repeat<PrefetchModel.Book>(null, 1)).Prefetch(b => b.Title).AsAsyncEnumerable();
731+
.Concat(Enumerable.Repeat<PrefetchModel.Book>(null, 1)).Prefetch(b => b.Title);
733732
var titleField = Domain.Model.Types[typeof(PrefetchModel.Book)].Fields[nameof(PrefetchModel.Book.Title)];
734733
var titleType = Domain.Model.Types[typeof(PrefetchModel.Title)];
735734
var count = 0;
@@ -783,7 +782,7 @@ public async Task NestedPrefetchWhenChildElementIsNullAsyncTest()
783782

784783
await using (var tx = session.OpenTransaction()) {
785784
var prefetcher = session.Query.All<PrefetchModel.Book>()
786-
.Prefetch(b => b.Title.Book).AsAsyncEnumerable();
785+
.Prefetch(b => b.Title.Book);
787786
var titleField = Domain.Model.Types[typeof(PrefetchModel.Book)].Fields[nameof(PrefetchModel.Book.Title)];
788787
var titleType = Domain.Model.Types[typeof(PrefetchModel.Title)];
789788
await foreach (var book in prefetcher) {
@@ -837,7 +836,7 @@ public async Task NestedPrefetchWhenRootElementIsNullAsyncTest()
837836

838837
await using (var tx = session.OpenTransaction()) {
839838
var books = session.Query.All<PrefetchModel.Book>().AsEnumerable().Concat(Enumerable.Repeat<PrefetchModel.Book>(null, 1))
840-
.Prefetch(b => b.Title.Book).AsAsyncEnumerable();
839+
.Prefetch(b => b.Title.Book);
841840
var titleField = Domain.Model.Types[typeof(PrefetchModel.Book)].Fields[nameof(PrefetchModel.Book.Title)];
842841
var titleType = Domain.Model.Types[typeof(PrefetchModel.Title)];
843842
var count = 0;
@@ -883,7 +882,7 @@ public async Task StructureFieldsPrefetchAsyncTest()
883882
await using (var tx = session.OpenTransaction()) {
884883
var containers = session.Query.Many<PrefetchModel.OfferContainer>(Enumerable.Repeat(containerKey, 1))
885884
.Prefetch(oc => oc.RealOffer.Book)
886-
.Prefetch(oc => oc.IntermediateOffer.RealOffer.BookShop).AsAsyncEnumerable();
885+
.Prefetch(oc => oc.IntermediateOffer.RealOffer.BookShop);
887886
await foreach (var key in containers) {
888887
PrefetchTestHelper.AssertOnlyDefaultColumnsAreLoaded(book0Key, book0Key.TypeInfo, session);
889888
PrefetchTestHelper.AssertOnlyDefaultColumnsAreLoaded(bookShop1Key, bookShop1Key.TypeInfo, session);
@@ -917,7 +916,7 @@ public async Task StructurePrefetchAsyncTest()
917916
await using (var session = await Domain.OpenSessionAsync())
918917
await using (var tx = session.OpenTransaction()) {
919918
var containers = session.Query.Many<PrefetchModel.OfferContainer>(Enumerable.Repeat(containerKey, 1))
920-
.Prefetch(oc => oc.IntermediateOffer).AsAsyncEnumerable();
919+
.Prefetch(oc => oc.IntermediateOffer);
921920
await foreach (var key in containers) {
922921
PrefetchTestHelper.AssertOnlySpecifiedColumnsAreLoaded(containerKey, containerKey.TypeInfo, session,
923922
field => PrefetchTestHelper.IsFieldToBeLoadedByDefault(field) || field.Name.StartsWith("IntermediateOffer"));

Orm/Xtensive.Orm/Linq/ExpressionExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public static class ExpressionExtensions
2929
private static readonly Func<Type, MethodInfo> TupleValueAccessorFactory;
3030

3131
private static readonly Type MemoryExtensionsType = typeof(MemoryExtensions);
32-
3332
private static readonly int[] MemoryExtensionsContainsMethodTokens;
3433
private static readonly MethodInfo EnumerableContains;
3534

0 commit comments

Comments
 (0)