SaveChangesAsync not working inside PUT method - c#

I am Using ODataController to insert and Update Entries to Database
I am hitting the PUT method , using .UpdateEntryAsync()
Here is my Put Method.
public async Task<IHttpActionResult> Put([FromODataUri] string key, Delta<KYCBtnDetails> patch)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
KYCBtnDetails KYCBtnDetails = await _KYCDBModel.KYCBtnDetails.FindAsync(key);
if (KYCBtnDetails == null)
{
return NotFound();
}
patch.Put(KYCBtnDetails);
KYCBtnDetails.id = key;
try
{
await _KYCDBModel.SaveChangesAsync();
}
catch (Exception e)
{
}
return Updated(KYCBtnDetails);
}
catch (Exception ex)
{
}
return null;
}
on await _KYCDBModel.SaveChangesAsync() it give me error as
"New Transaction is not allowed because there are other threads running in the session"
"An error occurred while starting a transaction on the provider connection. See the inner exception for details."
Sometimes it runs and sometimes it gives error
Please Help me on this,
Thank You.

By convention, here's how the Put controller action signature is expected to look like:
public async Task<IHttpActionResult> Put([FromODataUri] string key, [FromBody]KYCBtnDetails kycBtnDetails)
{
// ...
}
If you decide to use Patch, here is how the controller action signature is expected to look like:
public async Task<IHttpActionResult> Patch([FromODataUri] string key, [FromBody]Delta<KYCBtnDetails> patch)
{
// ...
}
Take specific note of FromBody attribute

Related

How to throw error (Bad Request) in void method in Web API

How to throw error (Bad Request) in void method in Web API
public async Task UpdateRecord([FromBody] UpdateRecord model)
{
try
{
await _recordService.UpdateRecord(model);
}
catch (Exception ex)
{
var message = new NetHttp.HttpResponseMessage(HttpStatusCode.BadRequest);
message.Content = new NetHttp.StringContent(ex.Message);
throw new WebHttp.HttpResponseException(message);
}
}
In swagger, its showing as 500. But I have mentioned 400 - Bad Request
Since you are throwing an exception it will always be 500 (if you are not catching it anywhere else).
If you want a bad request, then
public async Task<IHttpActionResult> UpdateRecord([FromBody] UpdateRecord model)
{
try
{
await _recordService.UpdateRecord(model);
}
catch (Exception ex)
{
return BadRequest(/*Optional message*/);
}
return Ok(returnData);
}
I would advise you though to rethink your strategy as:
Unhandled exceptions should not be happening normally in your application. Should they happen, then the 500 family is the proper Http code to return.
You can see that BadRequest expects some validation errors. While not mandatory, this error code assumes there is some type of wrong input data in terms of format and not business logic.
You can though serve this without a validation message
return BadRequest();
I have updated as below after got advise from Athanasios Kataras.
public async Task<IActionResult> UpdateRecord([FromBody] UpdateRecord model)
{
try
{
await _recordService.UpdateRecord(model);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Ok(); // Not passing any value inside
}

Multiple ProducesResponseType for same status code 200OK

I am using swagger ui and I have action like this in my controller:
[HttpGet("{id}")]
[AllowAnonymous]
[ProducesResponseType(typeof(PublicDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(PrivateDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUser(string id)
{
try
{
...
if (...)
{
var dto = _mapper.Map<PrivateDto>(user);
return Ok(dto);
}
else
{
var dto = _mapper.Map<PublicDto>(user);
return Ok(dto);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error inside action");
return StatusCode(500, "Internal server error");
}
}
The problem is that on my swagger UI page I can see only one of them in Responses area, and got scheme for only one. Is there a way to have more than one objects for response for one status code?
This is not a bug it’s a limitation of the open api specification, which SB is built on. Specifically you can only have one response object per response code. See https://spec.openapis.org/oas/v3.1.0#responses-object

Rest API No action was found on the controller

I call your experience to help me solve a problem.
I have this code:
public class FanController : ApiController
{
[ActionName("ImportAwb")]
[HttpPost]
public async Task<object> ImportAwb([FromBody]JObject data)
{
try
{
string username = data["username"].ToString();
string clientId = data["clientId"].ToString();
string userPass = data["userPass"].ToString();
string fisier = data["fisier"].ToString();
var responseString = await FanCourier.ImportAwbIntegrat(username, clientId, userPass, fisier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
If i left just one method like this, i can call it from Postman with no problem, but if i try to make another one, like this:
{
public class FanController : ApiController
{
[ActionName("ImportAwb")]
[HttpPost]
public async Task<object> ImportAwb([FromBody]JObject data)
{
try
{
string username = data["username"].ToString();
string clientId = data["clientId"].ToString();
string userPass = data["userPass"].ToString();
string fisier = data["fisier"].ToString();
var responseString = await FanCourier.ImportAwbIntegrat(username, clientId, userPass, fisier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
[ActionName("PrintareAwbHtml")]
[HttpPost]
public async Task<object> PrintareAwbHtml([FromBody]FanCourier fanCourier)
{
try
{
var responseString =
await fanCourier.PrintareAwbHtml(fanCourier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
The response from Postman call is:
"Multiple actions were found that match the request: \r\nImportAwb on type Courier.Rest.Controllers.FanController\r\nPrintareAwbHtml on type Courier.Rest.Controllers.FanController"
I was tried to add a [Route("api/[controller]")] before public class FanController : ApiController and the error was change to:
No action was found on the controller 'Fan' that matches the request.
I have tried to find something on the internet but i found nothing to help my situations.
You should define routes for each of your actions so you know which will be called when specific API is called.
Use [Route] tag to accomplish that
[ActionName("ImportAwb")]
[HttpPost]
[Route("Action1")]
public async Task<object> ImportAwb([FromBody]JObject data)
{
...
And from postman, call your endpoint with url being. http://yoururl.com/Action1.
(YourUrl would be the path you set up for this controller... might include /api or what you might have configured. Add /Action1 to the end of that url)
You can have multiple routes to the same URL as long as they are different methods (post, get, delete, patch etc.).

ASP.Net & EF Core 2 and Concurrency token error

Entity Framework Core 2, added a concurrency token property to model
[Timestamp]
public byte[] Timestamp { get; set; }
Controller Edit fails
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, [Bind("Id,Name,Description,IsDeleted,ParentId")] ItemStatus itemStatus)
{
if (id != itemStatus.Id)
return NotFound();
if (ModelState.IsValid)
{
try
{
_context.Update(itemStatus);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ItemStatusExists(itemStatus.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["ParentId"] = new SelectList(_context.ItemStatus, "Id", "Description", itemStatus.ParentId);
return View(itemStatus);
}
the specific error I am receiving occurs when SaveChangesAsync occurs. catch pops and when I step in it goes straight to the throw.
DbUpdateConcurrencyException: Database operation expected to affect 1
row(s) but actually affected 0 row(s). Data may have been modified or
deleted since entities were loaded. See
http://go.microsoft.com/fwlink/?LinkId=527962 for information on
understanding and handling optimistic concurrency exceptions.
Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(int
commandIndex, int expectedRowsAffected, int rowsAffected)
searching for the error message doesn't help. did find this article but it appears to be no help.
https://learn.microsoft.com/en-us/ef/core/saving/concurrency
as indicated in the comments, I was missing a hidden field to 'save' the timestamp in the view.
Followed this example: https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/concurrency
for clarity added my altered Edit. I had to do something similar to Delete too. this needs to be added to the Edit view <input type="hidden" asp-for="Timestamp" />
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, [Bind("Id,Name,Description,ParentId,Timestamp")] ItemStatus itemStatus)
{
if (id != itemStatus.Id)
return NotFound();
if (ModelState.IsValid)
{
try
{
_context.Update(itemStatus);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ItemStatusExists(itemStatus.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["ParentId"] = new SelectList(_context.ItemStatus, "Id", "Description", itemStatus.ParentId);
return View(itemStatus);
}

Stuck with Async Task and Await

I don't know whats wrong with this code, but still it doesn't respond the needed value.
Here is my sample code:
WebAPI 2:
KOTController.cs
[HttpGet]
[Route("testasync")]
public IHttpActionResult TestAsync()
{
try
{
return Ok(_iKOTManager.TestAsync());
}
catch (Exception ex)
{
logger.Error(ex);
return null;
}
}
Interface
IKOTManager.cs
Task<int> TestAsync();
KOTManager.cs
public async Task<int> TestAsync()
{
return await Task.Run<int>(() =>
{
return 999 + 23;
});
}
When I send a request to this API, it returns something like this not the number
<TaskOfint xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/System.Threading.Tasks"/>
Any advice would be really helpful. Thank you.
You get that response because you are return the Task and not actually executing it. The framework is just serializing the task object and returning that.
Refactor the action to return Task and then await the method on interface
[HttpGet]
[Route("testasync")]
public async Task<IHttpActionResult> TestAsync() {
try {
return Ok(await _iKOTManager.TestAsync());
} catch (Exception ex) {
logger.Error(ex);
return InternalServerError();
}
}
Reference: Async/Await - Best Practices in Asynchronous Programming
Additionally, in the case of the exception you should return an appropriate result instead of null. This should eventually be refactored out as handling errors within the controller is considered a cross-cutting concern. Controllers should as lean as possible.
[HttpGet]
[Route("testasync")]
public async Task<IHttpActionResult> TestAsync() {
return Ok(await _iKOTManager.TestAsync());
}
You need to change your controller to be async and then await the task. i.e.
public async Task<IHttpActionResult> TestAsync()
...
return Ok(await _iKOTManager.TestAsync());
See the documentation for async/await here.
Your action method is consuming/calling async method, you need to make your action method as well async to return Task<T> as return type like:
[HttpGet]
[Route("testasync")]
public async Task<IHttpActionResult> TestAsync()
{
try
{
return Ok( await _iKOTManager.TestAsync());
}
catch (Exception ex)
{
logger.Error(ex);
return null;
}
}
If we are calling an async method, if possible we should make the calling side method as well async and return Task<T> in all call chains.
Hope it helps!

Categories

Resources