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;
}
Related
I'm trying to write Api's using xamarion and do crud Operation in firebase real time database, But this is my first time to deal with it now i want to test this api's without need to write xaml code and create form for example, How can I do it ? in node js and web development we use postman, here what we can do ?
this is my code for example :
public async Task<bool> Save(Specalist specalist)
{
var data = await firebaseClient.Child(nameof(Specalist)).PostAsync(JsonConvert.SerializeObject(specalist));
if (string.IsNullOrEmpty(data.Key))
{
return true;
}
return false;
}
public async Task<List<Specalist>> GetAll()
{
return (await firebaseClient.Child(nameof(Specalist)).OnceAsync<Specalist>()).Select(item => new Specalist
{
Email = item.Object.Email,
Name = item.Object.Name,
ID = item.Object.ID
}).ToList();
}
public async Task<bool> Update(Specalist specalist)
{
await firebaseClient.Child(nameof(Specalist)+"/"+specalist.ID).PatchAsync(JsonConvert.SerializeObject(specalist));
return true;
}
public async Task<bool> Delete(string id)
{
await firebaseClient.Child(nameof(Specalist)+"/"+id).DeleteAsync();
return true;
}
I am creating an async task in a repo, and then accessing it from the controller to try and minimize the logic in the controller.
The thing is, that when I used the exact same logic in the controller, there was an output, now that I'm accessing this way, it isn't searching for it.
Repository:
public async Task<List<Guests>> GetGuesteByAsync(string Search)
{
var list = GetAll();
var listquery = from x in context.Guest select x;
if (!string.IsNullOrEmpty(Search))
{
listquery = listquery.Where(x => x.FullName.Contains(Search) || x.Organisation.Contains(Search));
}
return await context.Guests.ToListAsync();
}
The controller:
[HttpGet]
public async Task<ActionResult> Index(string Search)
{
ViewData["Getsearchinput"] = EmpSearch;
//List<Guests> listofguests = await repository.GetGuesteByAsync(Search);
var res = await (repository.GetGuesteByAsync(Search));
return View(res);
}
This returns all entries.
return await context.Guests.ToListAsync();
I think you should use your query. Can you try this?
return await listquery.ToListAsync();
I am writing controllers in Web API 2, against which odata queries will be executed:
[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount()
{
var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
var response = _accountService.Get(query);
if (!response.Result.IsSuccessStatusCode)
{
return NotFound();
}
var readAsAsync = response.Result.Content.ReadAsAsync<object>();
if (readAsAsync == null)
{
return NotFound();
}
var result = await readAsAsync;
return Ok(result);
}
How do I inject the Request, specifically as it relates to: var query = Request.RequestUri.PathAndQuery.Split('/')[2]; ?
Here's a very basic test that I've written for this controller:
[TestMethod]
public void GetAccount_Returns_IHttpActionResultTask()
{
var accountsService = new Mock<IAccountService>();
var sut = new AccountsController(accountsService.Object);
Assert.IsInstanceOfType(sut.GetAccount(), typeof(Task<IHttpActionResult>));
}
In order to test with different values for Request.RequestUri...., how do I rewrite my controller to be more testable?
Set the Request property on the ApiCntroller.
[TestMethod]
public async Task GetAccount_Returns_IHttpActionResult() {
//Arrange
var accountsService = new Mock<IAccountService>();
var sut = new AccountsController(accountsService.Object);
sut.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/accounts?filter=name")
};
//Act
var result = await sut.GetAccount();
//Assert
Assert.IsInstanceOfType(result, typeof(IHttpActionResult));
}
Also there or some potential blocking issues with the method under test. Mixing async/await with .Result blocking calls can cause deadlocks.
Refactor:
[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount() {
var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
var response = await _accountService.Get(query);
if (!response.IsSuccessStatusCode) {
return NotFound();
}
var readAsAsync = response.Content.ReadAsAsync<object>();
if (readAsAsync == null) {
return NotFound();
}
var result = await readAsAsync;
return Ok(result);
}
I have a WebApi controller which calls a third party API in asynchronous mode.
All works ok and now I want to sort the result in a separate action method.
Now, when I call the API, the callback with the result never happens after running "await client.GetAsycn(...)" in the DAL. What am I missing?
This is my API controller:
// GET api/lookup
[ResponseType(typeof(RestaurantModel))]
public async Task<IHttpActionResult> Get(string outcode)
{
if (string.IsNullOrEmpty(outcode)) throw new ArgumentNullException(nameof(outcode));
var result = await _repository.GetRestaurantsByOutcode(outcode);
return Ok(new RestaurantModel()
{
Result = result
});
}
// GET api/sorted
[System.Web.Http.Route("~/api/sorted")]
public List<Restaurant> GetSorted(string outcode)
{
if (string.IsNullOrEmpty(outcode)) throw new ArgumentNullException(nameof(outcode));
return _repository.GetSortedRestaurantsByOutcode(outcode);
}
This is my repository with a new method to sort the result:
public class RestaurantRepository : IRestaurantRepository
{
private readonly IContext _context;
public RestaurantRepository(IContext context)
{
_context = context;
}
public Task<ApiResult> GetRestaurantsByOutcode(string outcode)
{
return _context.GetRestaurantsByOutcode(outcode);
}
public List<Restaurant> GetSortedRestaurantsByOutcode(string outcode)
{
return _context.GetRestaurantsByOutcode(outcode).Result.Restaurants
.OrderBy(x => x.Name).ToList();
}
}
This is my DAL to call the third party API:
public async Task<ApiResult> GetRestaurantsByOutcode(string outcode)
{
using (var client = new HttpClient())
{
ConfigureHttpClient(client);
var response = await client.GetAsync(
$"restaurants?q={WebUtility.UrlEncode(outcode)}");
return response.IsSuccessStatusCode
? await response.Content.ReadAsAsync<ApiResult>()
: null;
}
}
You have a mix-match of sometimes you use async/await and other times you don't. Async / await (can and does by default) ensures that the call resumes on the calling thread so the context is resulted. You need to allign your code so you make use of the async/await in the whole stack. Otherwise you are creating a deadlock for your self.
[System.Web.Http.Route("~/api/sorted")]
// missing async in signature (not good if you are calling it with await in your controller)
public async Task<List<Restaurant>> GetSorted(string outcode)
{
if (string.IsNullOrEmpty(outcode)) throw new ArgumentNullException(nameof(outcode));
// added await in call
return await _repository.GetSortedRestaurantsByOutcode(outcode);
}
DAL
public class RestaurantRepository : IRestaurantRepository
{
private readonly IContext _context;
public RestaurantRepository(IContext context)
{
_context = context;
}
// added async and await
public async Task<ApiResult> GetRestaurantsByOutcode(string outcode)
{
return await _context.GetRestaurantsByOutcode(outcode);
}
// added async and await
public async Task<List<Restaurant>> GetSortedRestaurantsByOutcode(string outcode)
{
// here you were not using await but then using result even though you were calling into a method marked as async which in turn used an await. this is where you deadlocked but this the fix.
return (await _context.GetRestaurantsByOutcode(outcode)).Restaurants
.OrderBy(x => x.Name).ToList();
}
}
I have sealed classes with async(.net 4.5) methods that need to be mocked. I'm using Microsoft Fakes and so they will be "shims". The following code is an example of what I need to do. It builds but when run and the "LoginAsync" method within the "Login" controller method is called, the test hangs.
[TestMethod]
public async Task LoginPost_Returns() {
using (ShimsContext.Create()) {
var c = new TestController();
var user=new User();
Fakes.ShimUserManager.AllInstances.LoginAsyncString = (um, u) => new Task<IUser>(() => { return user; });
//call controller method
var result = await c.Login(model, returnUrl) as ViewResult;
var expectedViewName = "Index";
Assert.IsNotNull(result);
Assert.AreEqual(expectedViewName, result.ViewName);
}
//Controller method
public async Task<ActionResult> Login(LoginModel model, string returnUrl) {
var user = await UserManager.LoginAsync(model.UserName, model.password);
return View();
}
Don't use the Task constructor in async code. If you just need a completed Task with a return value, use Task.FromResult:
IUser user = new User();
Fakes.ShimUserManager.AllInstances.LoginAsyncString = (um, u) => Task.FromResult(user);
As an additional tip, it's a good idea to cover these cases in your unit tests:
Synchronous success (Task.FromResult(user)).
Asynchronous success (Task.Run(() => user)).
Asynchronous error (Task.Run(() => { throw new InvalidOperationException("or whatever"); return user; })).
Fakes.ShimUserManager.AllInstances.LoginAsyncString =
(um, u) => new Task<IUser>(() => { return user; });
This creates an unstarted Task. Your code hangs, because the Task is never started. To fix that, you can:
Use Task.Run() (or Task.Factory.StartNew()), which returns an already started Task.
Use Task.FromResult(). which returns an already completed Task.
Make the lambda async:
Fakes.ShimUserManager.AllInstances.LoginAsyncString = async (um, u) => user;