Skip to content

Commit e81cb9f

Browse files
committed
Add SingleOrDefaultAsync overload with single key value
1 parent 4111bba commit e81cb9f

3 files changed

Lines changed: 93 additions & 2 deletions

File tree

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

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ public void SingleOrDefaultByExistingIdTest()
327327
}
328328

329329
[Test]
330-
public async Task SingleOrDefaultByExistingIdAsyncTest()
330+
public async Task SingleOrDefaultByExistingIdAsyncTest1()
331331
{
332332
await RunWithinSessionAsync(async (s) => {
333333
var existingId = (int) existingKeys[customerType][0].Value.GetValue(0, out var _);
@@ -348,6 +348,28 @@ await RunWithinSessionAsync(async (s) => {
348348
});
349349
}
350350

351+
[Test]
352+
public async Task SingleOrDefaultByExistingIdAsyncTest2()
353+
{
354+
await RunWithinSessionAsync(async (s) => {
355+
var existingId = (int) existingKeys[customerType][0].Value.GetValue(0, out var _);
356+
var detector = new QueryExecutionDetector();
357+
using (detector.Attach(s)) {
358+
// Entity is not in cache yet
359+
var existingEntity = await s.Query.SingleOrDefaultAsync<Customer>(existingId);
360+
}
361+
Assert.That(detector.DbCommandsDetected, Is.True);
362+
detector.Reset();
363+
364+
using (detector.Attach(s)) {
365+
// now it is in cache
366+
var existingEntity = await s.Query.SingleOrDefaultAsync<Customer>(existingId);
367+
}
368+
Assert.That(detector.DbCommandsDetected, Is.False);
369+
detector.Reset();
370+
});
371+
}
372+
351373
[Test]
352374
public void SingleOrDefaultByInexistentIdTest()
353375
{
@@ -372,7 +394,7 @@ public void SingleOrDefaultByInexistentIdTest()
372394
}
373395

374396
[Test]
375-
public async Task SingleOrDefaultByInexistentIdAsyncTest()
397+
public async Task SingleOrDefaultByInexistentIdAsyncTest1()
376398
{
377399
await RunWithinSessionAsync(async (s) => {
378400
var inexistentId = 9999;
@@ -395,6 +417,29 @@ await RunWithinSessionAsync(async (s) => {
395417
});
396418
}
397419

420+
[Test]
421+
public async Task SingleOrDefaultByInexistentIdAsyncTest2()
422+
{
423+
await RunWithinSessionAsync(async (s) => {
424+
var inexistentId = 9999;
425+
426+
var detector = new QueryExecutionDetector();
427+
using (detector.Attach(s)) {
428+
var shouldBeNull = await s.Query.SingleOrDefaultAsync<Customer>(inexistentId);
429+
Assert.That(shouldBeNull, Is.Null);
430+
}
431+
Assert.That(detector.DbCommandsDetected, Is.True);
432+
detector.Reset();
433+
434+
using (detector.Attach(s)) {
435+
var shouldBeNull = await s.Query.SingleOrDefaultAsync<Customer>(inexistentId);
436+
Assert.That(shouldBeNull, Is.Null);
437+
}
438+
Assert.That(detector.DbCommandsDetected, Is.False);
439+
detector.Reset();
440+
await Task.CompletedTask;
441+
});
442+
}
398443

399444
private void RunWithinSession(Action<Session> testAction)
400445
{

Orm/Xtensive.Orm/Orm/Query.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,23 @@ public static Task<T> SingleOrDefaultAsync<T>(object[] keyValues, CancellationTo
385385
return Session.Demand().Query.SingleOrDefaultAsync<T>(keyValues, token);
386386
}
387387

388+
/// <summary>
389+
/// Resolves (gets) the <see cref="Entity"/> by the specified <paramref name="keyValue"/>
390+
/// in the current <see cref="Session"/>.
391+
/// </summary>
392+
/// <typeparam name="T">Type of the entity.</typeparam>
393+
/// <param name="keyValue">Key value.</param>
394+
/// <param name="token">The token to cancel this operation.</param>
395+
/// <returns>
396+
/// The <see cref="Entity"/> specified <paramref name="keyValue"/> identify.
397+
/// <see langword="null"/>, if there is no such entity.
398+
/// </returns>
399+
public static Task<T> SingleOrDefaultAsync<T>(object keyValue, CancellationToken token)
400+
where T : class, IEntity
401+
{
402+
return Session.Demand().Query.SingleOrDefaultAsync<T>(keyValue, token);
403+
}
404+
388405
#region Execute
389406

390407
/// <summary>

Orm/Xtensive.Orm/Orm/QueryEndpoint.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,23 @@ public async Task<T> SingleOrDefaultAsync<T>(object[] keyValues, CancellationTok
496496
return (T) (object) (await SingleOrDefaultAsync(GetKeyByValues<T>(keyValues), token).ConfigureAwait(false));
497497
}
498498

499+
/// <summary>
500+
/// Resolves (gets) the <see cref="Entity"/> by the specified <paramref name="keyValue"/>
501+
/// in the current <see cref="session"/>.
502+
/// </summary>
503+
/// <typeparam name="T">Type of the entity.</typeparam>
504+
/// <param name="keyValue">Key value.</param>
505+
/// <param name="token">The token to cancel this operation.</param>
506+
/// <returns>
507+
/// The <see cref="Entity"/> specified <paramref name="keyValues"/> identify.
508+
/// <see langword="null"/>, if there is no such entity.
509+
/// </returns>
510+
public async Task<T> SingleOrDefaultAsync<T>(object keyValue, CancellationToken token = default)
511+
where T : class, IEntity
512+
{
513+
return (T) (object) (await SingleOrDefaultAsync(GetKeyByValue<T>(keyValue), token).ConfigureAwait(false));
514+
}
515+
499516
/// <summary>
500517
/// Fetches multiple instances of specified type by provided <paramref name="keys"/>.
501518
/// </summary>
@@ -970,6 +987,18 @@ private Key GetKeyByValues<T>(object[] keyValues)
970987
return Key.Create(session.Domain, session.StorageNodeId, typeof(T), TypeReferenceAccuracy.BaseType, keyValues);
971988
}
972989

990+
private Key GetKeyByValue<T>(object keyValue)
991+
{
992+
ArgumentNullException.ThrowIfNull(keyValue);
993+
switch (keyValue) {
994+
case Key key:
995+
return key;
996+
case Entity entity:
997+
return entity.Key;
998+
}
999+
return Key.Create(session.Domain, session.StorageNodeId, typeof(T), TypeReferenceAccuracy.BaseType, keyValue);
1000+
}
1001+
9731002
private Expression BuildRootExpression(Type elementType)
9741003
{
9751004
return RootBuilder!=null

0 commit comments

Comments
 (0)