I have a method:
public async Task<int> CountCust(IQueryable<Cust> cust, CancellationToken cancellationToken)
{
......
......
totalCount = await cust.Where(.....).CountAsync(cancellationToken);
......
......
}
Cust is class:
public class Cust
{
......
}
I've tried:
Cust cust = new();
var CustListQ = new List<Cust> { cust };
var CustListQ = CustListQ.AsQueryable();
And use CustListQ as the input argument. But it complains that CustListQ has not implemented Async.
System.InvalidOperationException: 'The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.'
So do I get a async CustListQ from the class ?
Thanks.
Related
Pretty straightforward question. I want to be able to delete multiple IDs, just like the example below.
public async Task<ActionResult> Delete(List<int> id)`
Snippet
[HttpDelete("{id:int}")]
public async Task<ActionResult> Delete(int id)
{
await Mediator.Send(new DeleteRoomCommand { Id = id }).ConfigureAwait(false);
return NoContent();
}
public class DeleteRoomCommand : IRequest
{
public long Id { get; set; }
}
public class DeleteRoomCommandHandler : IRequestHandler<DeleteRoomCommand>
{
private readonly IApplicationDbContext _context;
public DeleteRoomCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<Unit> Handle(DeleteRoomCommand request, CancellationToken cancellationToken)
{
var entity = await _context.Rooms.FindAsync(request.Id).ConfigureAwait(false);
if (entity == null)
{
throw new NotFoundException(nameof(Room), request.Id);
}
_context.Rooms.Remove(entity);
await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return Unit.Value;
}
}
You seem to be asking two questions:
How do I write a new command and handler for MediatR that will accept and pass an enumerable of IDs instead of just one?
How do I write a DbContext call to handle that enumerable?
The IRequest object is just a simple class that contains the data you need to process it. You can create a new pluralized command class that accepts an enumerable of IDs, and a new handler designed to handle that case. You'll need to tweak this based on your own testing.
public class DeleteRoomsCommand : IRequest
{
public IEnumerable<long> Ids { get; set; }
}
public class DeleteRoomsCommandHandler : IRequestHandler<DeleteRoomsCommand>
{
private readonly IApplicationDbContext _context;
public DeleteRoomCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<Unit> Handle(DeleteRoomsCommand request, CancellationToken cancellationToken)
{
var entities = await _context.Rooms.Where(r => request.Ids.Contains(r.Id)); // .ConfigureAwait(false);
_context.Rooms.RemoveRange(entities);
await _context.SaveChangesAsync(cancellationToken); // .ConfigureAwait(false);
return Unit.Value;
}
}
Your new call is instantiated the same, taking the IDs received by your controller and assigning them to a new MediatR command:
await Mediator.Send(new DeleteRoomsCommand { Ids = ids }); //.ConfigureAwait(false);
Your new method could accept the list of IDs via query parameter or via the body, depending on your need or convention.
I have the following test that uses a mock of a class. When I try to throw an exception it never actually throws the exception. It goes on as if the method is actually being called and I am unsure why.
Here is the test:
[Fact]
public async Task ReadResultSetShouldRetry()
{
// Arrange
_cosmosUtilWrapper.Setup(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(It.IsAny<FeedIterator>(), It.IsAny<ILogger>(), It.IsAny<CancellationToken>()))
.Throws(new Exception("It broke"));
var cosmosReadPolicy = new CosmosReadPolicy();
// Act
await Assert.ThrowsAsync<Exception>(async () => await CosmosRepository.ReadCosmosResultSetWithRetry<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, cosmosReadPolicy, CancellationToken.None));
// Assert
_cosmosUtilWrapper.Verify(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, default));
}
Here is the method that it is calling and I have it wrapped in a retry policy:
public static async Task<List<T>> ReadCosmosResultSetWithRetry<T>(
FeedIterator resultSet,
ILogger logger,
CosmosReadPolicy cosmosReadPolicy,
CancellationToken cancellationToken = default)
where T : class
{
CosmosUtilWrapper utilWrapper = new CosmosUtilWrapper();
var readCosmosResultSet = await cosmosReadPolicy.GetPolicy.ExecuteAsync(async () => await utilWrapper.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken));
return readCosmosResultSet;
}
Here is the CosmosUtilWrapper and below is the actual Cosmos Util class:
public class CosmosUtilWrapper
{
public virtual async Task<List<T>> ReadCosmosResultSet<T>(
FeedIterator resultSet,
ILogger logger,
CancellationToken cancellationToken = default)
where T : class
{
return await CosmosUtil.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken);
}
}
Here is the actual static Util method that is being returned in the above class. Had to go about it this way since this class is a static class and unit testing those are not very fun.
public static async Task<List<T>> ReadCosmosResultSet<T>(
FeedIterator resultSet,
ILogger logger,
CancellationToken cancellationToken = default)
where T : class
{
var foundDocuments = new List<T>();
while (resultSet.HasMoreResults)
{
using ResponseMessage responseMessage = await resultSet.ReadNextAsync(cancellationToken);
if (responseMessage.IsSuccessStatusCode)
{
using StreamReader streamReader = new StreamReader(responseMessage.Content);
using JsonTextReader textReader = new JsonTextReader(streamReader);
foundDocuments.AddRange((await JObject.LoadAsync(textReader, cancellationToken)).GetValue("Documents").ToObject<List<T>>());
}
else
{
throw new Exception($"Unable to read cosmos result set. Status code: {responseMessage.StatusCode}");
}
}
return foundDocuments;
}
Finally, Here is the message I get when running the tests
Message:
Assert.Throws() Failure
Expected: typeof(System.Exception)
Actual: (No exception was thrown)
It is because the mockup object _cosmosUtilWrapper in ReadResultSetShouldRetry() is never used in Task<List<T>> ReadCosmosResultSetWithRetry<T>.
In methode Task<List<T>> ReadCosmosResultSetWithRetry<T> you initialize a new object CosmosUtilWrapper utilWrapper = new CosmosUtilWrapper();. So, this object is not the same as the object above.
You can get the object from the mockup object with the following code: _cosmosUtilWrapper.Object. Pass this object in the function or in the constructor of the class when you remove the static from the methode.
For example:
public static async Task<List<T>> ReadCosmosResultSetWithRetry<T>(
FeedIterator resultSet,
ILogger logger,
CosmosReadPolicy cosmosReadPolicy,
CosmosUtilWrapper utilWrapper,
CancellationToken cancellationToken = default)
where T : class
{
var readCosmosResultSet = await cosmosReadPolicy.GetPolicy.ExecuteAsync(async () => await utilWrapper.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken));
return readCosmosResultSet;
}
For example Test:
[Fact]
public async Task ReadResultSetShouldRetry()
{
// Arrange
_cosmosUtilWrapper.Setup(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(It.IsAny<FeedIterator>(), It.IsAny<ILogger>(), It.IsAny<CancellationToken>()))
.Throws(new Exception("It broke"));
var cosmosReadPolicy = new CosmosReadPolicy();
var utilWrapper = _cosmosUtilWrapper.Object;
// Act
await Assert.ThrowsAsync<Exception>(async () => await CosmosRepository.ReadCosmosResultSetWithRetry<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, cosmosReadPolicy, utilWrapper, CancellationToken.None));
// Assert
_cosmosUtilWrapper.Verify(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, default));
}
Please see the synchronous code below:
public class PersonHandler : IRequestHandler<Person, Person>
{
public Task<Person> Handle(Person request, CancellationToken cancellationToken)
{
request.ID = 1;
request.Name = "Brian";
request.Age = 53;
return Task.FromResult(request);
}
}
and the calling code below:
var response2 = mediator.Send(new Person());
This works as expected. Please see the asynchronous code below:
public class PersonHandlerAsync : IRequestHandler<Person, Person>
{
public async Task<Person> Handle(Person request, CancellationToken cancellationToken)
{
request.ID = 1;
request.Name = "Brian";
request.Age = 53;
var result = await Task.FromResult(request);
await Task.Delay(30000);
return result;
}
}
and the calling code below:
var response = Task.Run(() => mediator.Send(new Person()));
This code works as expected.
Is it possible to have two handlers for the same class (Person) i.e. one synchronous and the other asynchronous? If I put both handler classes in my code, then both the mediator.send lines i.e. mediator.Send(new Person()); and Task.Run(() => mediator.Send(new Person())); call the synchronous method.
How can I tell mediatr, which handler class to use? i.e. PersonHandler for synchronous calls and PersonHandlerAsync for asynchronous calls.
You can't do it how you've specified.
Declare whatever flavor of handler you need - sync, async or cancellable async. From the IMediator side, the interface is async-only, designed for modern hosts.
However, one workaround would be to create a PersonRequest and PersonRequestAsync class, inherited from Person class:
public class Person
{
public int Id {get; set;}
public string Name {get; set;}
public int Age {get; set;}
}
//sync request
public class PersonRequest : Person, IRequest<Person>
{ }
//async request
public class PersonRequestAsync : Person, IRequest<Person>
{ }
Then, your handlers could look something like this:
public class PersonHandler : IRequestHandler<PersonRequest, Person>
{
public Task<Person> Handle(Person request, CancellationToken cancellationToken)
{
request.ID = 1;
request.Name = "Brian";
request.Age = 53;
return Task.FromResult(request);
}
}
And your async handler as follows:
public class PersonHandlerAsync : IRequestHandler<PersonRequestAsync, Person>
{
public async Task<Person> Handle(PersonRequestAsync request, CancellationToken cancellationToken)
{
request.ID = 1;
request.Name = "Brian";
request.Age = 53;
//not sure what this is? Hopefully it's just here for demo purposes!
var result = await Task.FromResult(request);
await Task.Delay(30000);
return result;
}
}
This effectively ties PersonRequestAsync to PersonHandlerAsync
I have an ASP.NET core application where I use a service layer between my controllers and Entity Framework.
Currently my services are synchronous, but I would like to make them asynchronous.
This is an controller example:
public async Task<IActionResult> Search(BusinessesSearchModel model)
{
var resultModel = await _businessService.Search(model);
return Ok(resultModel);
}
And this is an example of my service:
public interface IBusinessesService
{
Task<BusinessResultListModel> Search(BusinessesSearchModel model);
}
public class BusinessesService : IBusinessesService
{
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
var queryResult = (from b in MyDBContext.Businesses
where b.Name.Contains(model.Name)
&& b.Phone.Contains(model.Phone)
select new BusinessesListModel
{
ID = b.ID,
Name = b.Name,
Phone = b.Phone
}
).ToList();
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
}
This does not work. Visual Studio tells me:
This async method lacks 'await' operators and will run synchronously
Where should I add await to make this async?
Your code isn't doing anyting asynchronously. To utilize the await/async API, you need to be invoking an async method inside your own method (unless you want to implement the async logic yourself).
In your case, if you're using Entity Framework, you can use it's async methods for querying:
public class BusinessesService : IBusinessesService
{
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
var queryResult = await _context.YourEntity.Where(x => x.SomeProperty == model.Query).ToListAsync(); //<--- This is your async call, awaiting the ToListAsync() method
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
}
Other async methods in EntityFramework are FirstOrDefaultAsync(), SingleOrDefaultAsync(), SaveChangesAsync() and so on
you are missing the async within linq.
for example:
public async Task<IEnumerable<SchedulerJob>> GetAllByIdsAsync(IEnumerable<int> ids, CancellationToken cancellationToken = default(CancellationToken))
{
return await Context.SchedulerJobs
.Include(s => s.Program)
.Where(job => ids.Contains(job.Id))
.ToListAsync(cancellationToken);
}
the ToListAsync(cancellationToken); in combination with await, and you are set to go!
i see you edited the page:
you need to do:
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
//something like:
var queryResult = await (from b in MyDBContext.Businesses
where b.Name.Contains(model.Name)
&& b.Phone.Contains(model.Phone)
select new BusinessesListModel
{
ID = b.ID,
Name = b.Name,
Phone = b.Phone
}
).ToListAsync();
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
I want to simplify out repository code and want to use Funcs to provide common functionality.
The problem now is based on this code:
namespace Test
{
public class Demo
{
public Task<Person> GetAsync1(Guid id)
{
return ExecuteAsync(context => context.Persons.GetAsync(id));
}
public async Task<Person> GetAsync2(Guid id)
{
return await ExecuteAsync(async context => await context.Persons.GetAsync(id));
}
public Task<Person> GetAsync3(Guid id)
{
return ExecuteAsync(async context => await context.Persons.GetAsync(id));
}
public Task<TOut> ExecuteAsync(Func<Context, Task<TOut>> func)
{
using(var context = new Context())
{
return func(context);
}
}
}
}
The problem here for me is now how to call the async func correctly.
Which of those three Get methods is correct?
With the first one I think I get a deadlock because it hangs at this point. Number 2 works fine but I think two async/awaits here are not correct, because of the Task re-wrapping!?
Actually all 3 GetAsync implmentations where fine to do (personally I would use GetAsync1), what you have is a bug in ExecuteAsync
public async Task<TOut> ExecuteAsync(Func<Context, Task<TOut>> func)
{
using(var context = new Context())
{
return await func(context);
}
}
By awaiting the output of func you do not dispose of the context until the function has completed it's work.