Testing the Patch odata webapi method - c#

I need to test the following Patch method in my odata controller from my test project.
[ValidateModel]
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<User> patch)
{
var user = await db.Users.FindAsync(key);
if (user == null)
{
return NotFound();
}
patch.Patch(user);
Validate(user);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
db.Entry(user).Property(p => p.UserType).IsModified = false;
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserExists(key))
{
return NotFound();
}
throw;
}
return Updated(user);
}
The code in the test project is as follows. Could someone tell me how do I pass value to the Delta parameter. Currently I am getting compilation errors at line controller.Patch(1, user);.
[TestMethod]
public void TestPatch()
{
// Arrange
var controller = new UsersController();
var user = new User();
user.Id = 1;
user.Lastname = "Johanson";
// Act
controller.Patch(1, <System.Web.OData.Delta> user);
// Assert
}

You can also declare the delta using the dynamic keyword and set the properties directly:
dynamic delta = new Delta<User>();
delta.Id = 1;
delta.Lastname = "Johanson";

var delta = new Delta<User>(typeof(User));
delta.TrySetPropertyValue("Id", 1);
delta.TrySetPropertyValue("Lastname", "Johanson");
I don't know if there are any helpers to make that easier

#yenta's answer is perfectly fine, but if you can, also consider using the nameof (since C# 6.0)
var delta = new Delta<User>(typeof(User));
delta.TrySetPropertyValue(nameof(User.Id), 1);
delta.TrySetPropertyValue(nameof(User.Lastname), "Johanson");

Related

ASP.NET Core - How to update existing record and insert a new one at the same time using Entity Framework

In ASP.NET Core-6 Web API Entity Framework, I want the application to perform update and insert on the same model in the database at the same time.
I have this code:
public async Task<Response<string>> CreateIdentiticationAsync(CreateIdentiticationDto model)
{
var response = new Response<string>();
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
try
{
var identification = _mapper.Map<Identification>(model);
var existingIdentifications = await _dbContext.Identifications.Where(e => e.IsModified == false).ToListAsync();
foreach (var existingIdentification in existingIdentifications)
{
if (existingIdentification != null)
{
existingIdentification.IsModified = true;
_unitOfWork.UserIdentifications.Update(identification);
await _unitOfWork.Save();
}
}
Identification.IsModified = false;
Identification.Type = model.Type;
Identification.Name = model.Name;
await _unitOfWork.UserIdentifications.InsertAsync(identification);
await _unitOfWork.Save();
response.StatusCode = (int)HttpStatusCode.Created;
response.Successful = true;
response.Message = "Created Successfully!";
transaction.Complete();
return response;
}
catch (Exception ex)
{
transaction.Dispose();
response.Message = "An error occured";
response.Successful = false;
response.StatusCode = (int)HttpStatusCode.BadRequest;
return response;
}
}
}
When user wants to insert a new record, I want the application to first check the model, if no record exists it should just insert the new record.
But if it exists, it should update all the IsModified to true, and then go ahead and also insert a new record.
However, when no record exists, I was able to insert new record.
But where I have issue is that when it wants to update and insert new record, I got this error:
Violation of PRIMARY KEY constraint 'PK_identifications'. Cannot insert duplicate key in object 'dbo.identifications'. The duplicate key value is (81fe9b8d-2d9c-4d49-8f92-22afe043e327).
Note: Id is Guid and autgenerated
How do I resolve this?
Thanks
Many things to consider here, in modern EF, I would not use a unitOfWork also a transaction is not needed unless very specific cases, you should also validate the ID from your incoming model value is null since ID is supposed to be autogenerated (most likely this is the source of your error). Since I don't know the name of your PK field, I will not introduce such validation in my proposed code.
One last recommendation, avoid this code on your controller, it breaks Single Responsibility Principle. Create a layer where you perform your business tasks and call this layer from your controller.
Here is a simplified proposed code which should work, don't forget to ensure the value in the model ID is coming as null.
public async Task<Response<string>> CreateIdentiticationAsync(CreateIdentiticationDto model)
{
var response = new Response<string>();
try
{
var identification = _mapper.Map<Identification>(model);
var existingIdentifications = await _dbContext.Identifications.Where(e => e.IsModified == false).ToListAsync();
foreach (var existingIdentification in existingIdentifications)
{
existingIdentification.IsModified = true;
}
Identification.IsModified = false;
// If you already mapped this in line 6 why map those two fields again manually?
// Identification.Type = model.Type;
// Identification.Name = model.Name;
_dbContext.UpdateRange(existingIdentifications);
_dbContext.Add(identification);
await _dbContext.SaveChangesAsync();
response.StatusCode = (int)HttpStatusCode.Created;
response.Successful = true;
response.Message = "Created Successfully!";
return response;
}
catch (Exception ex)
{
transaction.Dispose();
response.Message = "An error occured";
response.Successful = false;
response.StatusCode = (int)HttpStatusCode.BadRequest;
return response;
}
}
public IActionResult Update(int? id,UpdateEmployeeVM employeeVM)
{
ViewBag.Positions = new SelectList(_context.Positions, nameof(Position.Id), nameof(Position.Name));
Employee existed=_context.Employees.Find(id);
if(existed==null) { NotFound();}
if (!_context.Positions.Any(p => p.Id == employeeVM.PositionId)) ModelState.AddModelError("PoitionId", "Choose Id right");
if (!ModelState.IsValid)
{
ViewBag.Positions = new SelectList(_context.Positions, nameof(Position.Id), nameof(Position.Name));
return View();
}
if (employeeVM.Image!=null)
{
string result = employeeVM.Image.CheckValidate("image/", 300);
if (result.Length > 0)
{
ViewBag.Positions=new SelectList(_context.Positions,nameof(Position.Id),nameof(Position.Name));
ModelState.AddModelError("Image", result);
}
existed.ImageUrl.DeleteFile(_env.WebRootPath, "assets/img");
existed.ImageUrl = employeeVM.Image.SaveFile(Path.Combine(_env.WebRootPath, "assets", "img"));
return View();
}
existed.PositionId=employeeVM.PositionId;
existed.FullName=employeeVM.FullName;
existed.ImageUrl = employeeVM.Image.SaveFile(Path.Combine(_env.WebRootPath, "assets", "img"));
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
public async Task<IActionResult> Create(CreateEmployeeVM employeeVM)
{
if (!_context.Positions.Any(p => p.Id == employeeVM.PositionId)) ModelState.AddModelError("PositionId", "bele bir position yoxdu");
if (!ModelState.IsValid)
{
ViewBag.Positions = new SelectList(_context.Positions, nameof(Position.Id), nameof(Position.Name));
return View();
}
string result = employeeVM.Image.CheckValidate(500,"image/");
if (result.Length > 0)
{
ViewBag.Positions = new SelectList(_context.Positions, nameof(Position.Id), nameof(Position.Name));
ModelState.AddModelError("Image", result);
return View();
}
Employee employee = new Employee
{
Name = employeeVM.Name,
Surname = employeeVM.Surname,
PositionId = employeeVM.PositionId,
FacebookUrl = employeeVM.FacebookUrl,
InstagramUrl = employeeVM.InstagramUrl,
TwitterUrl = employeeVM.TwitterUrl,
Salary = employeeVM.Salary,
ImageUrl = employeeVM.Image.SaveFile(Path.Combine(_env.WebRootPath, "assets", "img"))
};
await _context.Employees.AddAsync(employee);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}

C# FirstOrDefault() return null even with element existing

I have the following code that needs to return a single value. However, I keep on getting null (the field Notifications_Id = 0) even though it clearly exists in the database.
var role = _context.AssignedTickets.FirstOrDefault(a => a.Notifications_Id == incidence.Notifications_Id);
I am using Net Core 5
Below is my code
public async Task<ActionResult> EditTicket(int? id)
{
{
if (id == null)
{
return StatusCode(400);
}
Incidence incidence = await _context.Incidences.FindAsync(id);
if (incidence == null)
{
return StatusCode(401);
}
return View(incidence);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditTicket(Incidence incidence)
{
var role = _context.AssignedTickets.FirstOrDefault(a => a.Notifications_Id == incidence.Notifications_Id);
if (role == null)
{
return StatusCode(404);
}
role.Status = "C";
role.ClosedOn = DateTime.Now;
if (ModelState.IsValid)
{
DateTime Timeup = (DateTime)role.ClosedOn;
DateTime Timedown = (DateTime)role.CreatedOn;
long DiffTicks = (Timedown - Timeup).Ticks;
role.TurnAroundTime = Math.Abs(DiffTicks).ToString();
_context.Entry(incidence).State = EntityState.Modified;
_context.SaveChangesAsync();
return RedirectToAction("Dashboard", "Agent");
}
return View(incidence);
}
Please use var role = _context.AssignedTickets.FirstOrDefault(a => a.Notifications_Id == 29); to get the object, and then compare whether the attributes inside match the database.
I read your post and all comments carefully. What everyone said is very reasonable, and the code in the post is not problematic.
So I think the problem is likely to be a problem with the string connecting to the database, that is, a different database is used.

Testing web api controller with ActionResult

I try to get result of GetAll() function in unit test, but i can't convert this to list. How can i do it correctly ?
Test:
[Fact]
public async Task GetAllHeroes_ShouldReturnAllHeroes()
{
var controller = new HeroesController(_heroes);
var response = await controller.GetHeroes() as List<Hero>;
//here i need response list, but there's error
}
Controller:
// GET: api/v1/heroes
[HttpGet]
[Produces(typeof(List<Hero>))]
public async Task<ActionResult<IEnumerable<Hero>>> GetHeroes()
{
var result = await _heroes.GetAll();
return Ok(result);
}
You need to parse the response to a list of your model
like this
var response = await controller.GetHeroes();
Assert.IsType<OkObjectResult>(result);
var content = ((OkObjectResult)result).Value;
Assert.IsType<List<Hero>>(content);
var Heros = (List<Hero>)content;
I added some other asserts that may be useful
Does something like this work:
// Act
var result = await controller.GetHeroes();
var response = result as OkNegotiatedContentResult<IEnumerable<Hero>>;
// Assert
// Assert.IsNotNull(response);
var heroes = response.Content.Result;
Assert.IsInstanceOfType(heroes, typeof(IEnumerable<Hero>), "Incorrect Types");
var dtos = heroes.ToArray();
// Asserting output
Assert.AreEqual(1, dtos.Length);
Assert.AreEqual(expected[0].id, dtos[0].id);
For get IEnumerable, that is return to Action:
var response = await controller.GetHeroes();
var heroList = (IEnumerable<Hero>)response.Result;
More info here

(Async & await) vs (without Async & await) in Web API

I am new with Async and await using C# Programming. In WebAPI, we have created two API Controllers one with Async and await Programming and other is without that. We have done load testing using JMeter and we have got following results.
Users Sync Async
100 No Errors No Errors
500 No Errors No Errors
750 No Errors Errors - (59.0 %) - 502 Bad Gateway
763 No Errors Errors
764 No Errors Errors
765 Errors - (0.13 %) - 502 Bad Gateway Errors
1000 Errors Errors
Can you any please explain/suggest which approach is best or how can we proceed ?
API Code :
GetPersonalDetailsController - Async and await Used
public async Task<IHttpActionResult> GET([FromUri] RequestQueryListDTO objAPIRequest)
{
DateTime startResponseTime = DateTime.Now;
Response objResponse = null;
string strResponse = string.Empty;
var HeaderType = Request.Content.Headers.ContentType;
ProductBAL objProductBAL = null;
try
{
if (objAPIRequest != null)
{
Task<Response> tskGetProductDetails = Task<Response>.Run(() =>
{
objProductBAL = new ProductBAL();
return objProductBAL.GetProductDetails(objAPIRequest);
//Business Access Layer Logic calling
});
objResponse = await tskGetProductDetails;
}
else
{
objResponse = new Response();
objResponse.ReturnCode = -1;
objResponse.ReturnMessage = "Missing Parameters.";
}
}
catch (Exception ex)
{
\\ Exception Logging
}
finally
{
objProductBAL = null;
}
objResponse.ResponseTime = Math.Round((DateTime.Now - startResponseTime).TotalMilliseconds).ToString();
if (objResponse.ReturnCode == Convert.ToInt32(General.ReturnCode))
{
return Content<Response>(HttpStatusCode.BadRequest, objResponse);
}
else
{
return Ok(objResponse);
}
}
========================================================================
GetPDPController - Without using Async and await
public IHttpActionResult GET([FromUri] RequestQueryListDTO objAPIRequest)
{
DateTime startResponseTime = DateTime.Now;
Response objResponse = null;
string strResponse = string.Empty;
var HeaderType = Request.Content.Headers.ContentType;
try
{
if (objAPIRequest != null)
{
//Business Access Layer Logic calling
}
else
{
objResponse = new Response();
objResponse.ReturnCode = -1;
objResponse.ReturnMessage = "Missing Parameters.";
}
}
catch (Exception ex)
{
// Exception Logging Code
}
finally
{
objProductBAL = null;
}
objResponse.ResponseTime = Math.Round((DateTime.Now - startResponseTime).TotalMilliseconds).ToString();
if (objResponse.ReturnCode == Convert.ToInt32(General.ReturnCode))
{
return Content<Response>(HttpStatusCode.BadRequest, objResponse);
}
else
{
return Ok(objResponse);
}
}
My suggestion is have two methods, one Async and one not. That way you can test more.
GetProductDetails
GetProductDetailsAsync
you would then need to change the signature of the calling method aka GET
public IHttpActionResult GET([FromUri] RequestQueryListDTO objAPIRequest)
{
var objResponse = new Response();
//check the properties of objAPIRequest
if(bad)
{
//add stuff if you want
return Content<Response>(HttpStatusCode.BadRequest, objResponse);
}
//Business Access Layer Logic calling
//-----------------------
ProductBAL objProductBAL = new ProductBAL();
//you need to change this to async
var productDetails = objProductBAL.GetProductDetails(objAPIRequest);
//-----------------------
return Ok(objResponse);
}

Why detailed error message is not passed to HttpClient?

I am using the default Web Api controller that is auto generated. I am testing the validation and error handling in the client side. But somehow I have realised that the detail error message is not passed to client. Either if I throw HttpResponseException or returning IHttpActionResult in both cases the client is seeing only "Bad Request" but not the detailed message. Can anyone explain what is going wrong please?
public IHttpActionResult Delete(int id)
{
if (id <= 0)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Id should be greater than zero.", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response) // Either this way
}
var itemToDelete = (from i in Values
where i.Id == id
select i).SingleOrDefault();
if (itemToDelete == null)
{
return BadRequest(string.Format("Unable to find a value for the Id {0}", id)); // Or This way
}
Values.Remove(itemToDelete);
return Ok();
}
client code is as:
private async static Task DeleteValue(int id)
{
var url = "http://localhost:13628/api/Values/" + id;
using (var client = new HttpClient())
{
var response = await client.DeleteAsync(url);
if (response.IsSuccessStatusCode)
{
await ReadValues();
}
else
{
Console.WriteLine(response.ReasonPhrase);
Console.WriteLine(response.StatusCode);
}
}
}
None of the above works??
Thx
In your client side change Console.WriteLine(response.ReasonPhrase);
to Console.WriteLine(response.Content.ReadAsStringAsync().Result);
and it will give the detailed error message.
Replace below code into Web API delete action. Use HttpResponseMessage as return tpye for api instead of IHttpActionResult
[HttpDelete]
[Route("{id:int:min(1)}")]
public async Task<HttpResponseMessage> DeleteAsync(int id)
{
if(id < 0 )
{
return await Task.FromResult<HttpResponseMessage>(Request.CreateResponse<string>(HttpStatusCode.BadRequest, "Id should be greater than zero."));
}
try
{
var itemToDelete = (from i in Values
where i.Id == id
select i).SingleOrDefault();
if (itemToDelete == null)
{
return await Task.FromResult<HttpResponseMessage>(Request.CreateResponse<string>(HttpStatusCode.NotFound,
string.Format("Unable to find a value for the Id {0}", id)));
}
Values.Remove(itemToDelete);
return await Task.FromResult<HttpResponseMessage>(Request.CreateResponse(HttpStatusCode.OK));
}
catch (Exception ex)
{
return Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong."); // Default message if exception occured
}
}
And client Side:
private async static Task DeleteValue(int id)
{
var url = "http://localhost:13628/api/Values/" + id;
using (var client = new HttpClient())
{
var response = await client.DeleteAsync(url);
if (response.IsSuccessStatusCode)
{
await ReadValues();
}
else
{
var errorMessage = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await response.Content.ReadAsStringAsync());
// Here Newtonsoft.Json Package is used to deserialize response content
Console.WriteLine(errorMessage);
Console.WriteLine(response.StatusCode);
}
}
}
Above code is working at my side.

Categories