Skip to content

Commit

Permalink
(CommunityToolkit#173) Updated async methods in query.
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianhall committed Jan 13, 2025
1 parent f5777b9 commit b5f11b8
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 445 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,24 @@ public interface IRepository<TEntity> where TEntity : ITableData
/// <exception cref="HttpException">Thrown if the entity creation would produce a normal HTTP error.</exception>
/// <exception cref="RepositoryException">Thrown is there is an error in the repository.</exception>
ValueTask ReplaceAsync(TEntity entity, byte[]? version = null, CancellationToken cancellationToken = default);

/// <summary>
/// Executes a count against the query provided, which came from this data store. This allows you
/// to override the count operation to provide a more efficient count operation.
/// </summary>
/// <param name="query">The queryable being counted.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param>
/// <returns>The count of entities matching the query.</returns>
ValueTask<int> CountAsync(IQueryable query, CancellationToken cancellationToken = default)
=> ValueTask.FromResult(query.Cast<object>().Count());

/// <summary>
/// Executes a query retrieval against the query provided, which came from this data store. This allows you
/// to override the ToList operation to provide a more efficient operation.
/// </summary>
/// <param name="query">The queryable being executed.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param>
/// <returns>The entities matching the query.</returns>
ValueTask<List<object>> ToListAsync(IQueryable query, CancellationToken cancellationToken = default)
=> ValueTask.FromResult(query.Cast<object>().ToList());
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,23 @@ await WrapExceptionAsync(entity.Id, async () =>
_ = await Context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}, cancellationToken).ConfigureAwait(false);
}

/// <inheritdoc />
/// <remarks>
/// The entity framework core edition of this method uses the async method.
/// </remarks>
public virtual async ValueTask<int> CountAsync(IQueryable query, CancellationToken cancellationToken = default)
{
return await EntityFrameworkQueryableExtensions.CountAsync(query.Cast<object>(), cancellationToken).ConfigureAwait(false);
}

/// <inheritdoc />
/// <remarks>
/// The entity framework core edition of this method uses the async method.
/// </remarks>
public virtual async ValueTask<List<object>> ToListAsync(IQueryable query, CancellationToken cancellationToken = default)
{
return await EntityFrameworkQueryableExtensions.ToListAsync(query.Cast<object>(), cancellationToken).ConfigureAwait(false);
}
#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,34 @@ public virtual async Task<IActionResult> QueryAsync(CancellationToken cancellati
return BadRequest(validationException.Message);
}

// Note that some IQueryable providers cannot execute all queries against the data source, so we have
// to switch to in-memory processing for those queries. This is done by calling ToListAsync() on the
// IQueryable. This is not ideal, but it is the only way to support all of the OData query options.
IEnumerable<object>? results = null;
await ExecuteQueryWithClientEvaluationAsync(dataset, ds =>
List<object>? results = null;
await ExecuteQueryWithClientEvaluationAsync(dataset, async ds =>
{
results = (IEnumerable<object>)queryOptions.ApplyTo(ds, querySettings);
return Task.CompletedTask;
IQueryable query = queryOptions.ApplyTo(ds, querySettings);
// results = query.Cast<object>().ToList();
results = await Repository.ToListAsync(queryOptions.ApplyTo(ds, querySettings), cancellationToken).ConfigureAwait(false);

// If the request results in an ISelectExpandWrapper, then $select was used and
// the model will be incomplete. JSON rendering just turns this into a dictionary,
// so we'll do the same here.
if (results.Count > 0)
{
for (int i = 0; i < results.Count; i++)
{
if (results[i] is ISelectExpandWrapper wrapper)
{
results[i] = wrapper.ToDictionary();
}
}
}
});

int count = 0;
FilterQueryOption? filter = queryOptions.Filter;
await ExecuteQueryWithClientEvaluationAsync(dataset, async ds =>
{
IQueryable<TEntity> q = (IQueryable<TEntity>)(filter?.ApplyTo(ds, new ODataQuerySettings()) ?? ds);
count = await CountAsync(q, cancellationToken);
IQueryable<TEntity> q = (IQueryable<TEntity>)(queryOptions.Filter?.ApplyTo(ds, new ODataQuerySettings()) ?? ds);
// count = q.Cast<object>().Count();
count = await CountAsync(q, cancellationToken).ConfigureAwait(false);
});

PagedResult result = BuildPagedResult(queryOptions, results, count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Datasync.Server.Abstractions.Json;
using CommunityToolkit.Datasync.Server.OData;
using System.Text.Json;
using System.Text.Json.Serialization;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public AzureSqlEntityTableRepository_Tests(DatabaseFixture fixture, ITestOutputH

protected override bool CanRunLiveTests() => !string.IsNullOrEmpty(this.connectionString);

protected override Task<AzureSqlEntityMovie> GetEntityAsync(string id)
=> Task.FromResult(Context.Movies.AsNoTracking().SingleOrDefault(m => m.Id == id));
protected override async Task<AzureSqlEntityMovie> GetEntityAsync(string id)
=> await Context.Movies.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id);

protected override Task<int> GetEntityCountAsync()
=> Task.FromResult(Context.Movies.Count());
Expand Down
Loading

0 comments on commit b5f11b8

Please sign in to comment.