How to update existing child class by using the parent id - c#

Currently I have somes idea where we get the child data from its parent Id, and update the child data with hardcoded text. Parent Class:
`
public class Ride
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime RideStartTime { get; set; }
public string DestinationLongitude { get; set; }
public string DestinationLatitude { get; set; }
public int SeatAvailable { get; set; }
public Double TotalCost { get; set; } = 0;
public Double TotalDistance { get; set; } = 0;
//Ride has Many Request
public ICollection<Request> Requests { get; set; }
}
`
Child Class
public class Request : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string PickupLongitude { get; set; }
[Required]
public string PickupLatitude { get; set; }
public Double? EstimatedCost { get; set; } = 0;
public Double? Rating { get; set; } = 0;
public int RideId { get; set; }
public Ride Ride { get; set; }
}
The situation is when the when i need to update all child status column to "Confirm", i need to find it parent class first by search the rideId and if the ride found, it will update their child class attribute. Im using EF core to save the data.
// PUT api/<controller>/5
[HttpPut("{id}/confirm")]
public IActionResult ConfirmRide(int id, [FromBody]Ride ride)
{
try
{
if (ride.IsObjectNull())
{
_logger.LogError("Ride object sent from client is null.");
return BadRequest("Ride object is null");
}
if (!ModelState.IsValid)
{
_logger.LogError("Invalid ride object sent from client.");
return BadRequest("Invalid model object");
}
var dbRide = _repository.Ride.GetRideById(id);
if (dbRide == null)
{
_logger.LogError($"Ride with id: {id}, hasn't been found in db.");
return NotFound();
}
_repository.Ride.ConfirmRide(dbRide, ride, id, "Confirmed");
//_repository.Ride.
_repository.Save();
return NoContent();
}
catch (Exception ex)
{
_logger.LogError($"Something went wrong inside UpdateRide action: {ex.Message}");
return StatusCode(500, "Internal server error");
}
}
Currently this is my logic to save or update the data, can you guys help me how to update the child class base on parent Id.

How to add/update child entities when updating a parent entity in EF
I got this solution and modify it with other resource.
public void ConfirmRide(Ride dbRide, int id, string status)
{
dbRide.MapStatus(status);
Update(dbRide);
var existingParent = RepositoryContext.Rides
.Where(p => p.Id == id)
.Include(p => p.Requests).Where(r => r.Requests.Any( request => request.Status == "Approved"))
.SingleOrDefault();
if (existingParent != null)
{
foreach (var existingChild in existingParent.Requests.ToList())
{
existingChild.Status = "Confirmed";
}
}
RepositoryContext.SaveChanges();
}

Related

Bad request (400) when using PutAsJsonAsync if Model has navigation properties

I am creating a simple API and I have a method that handles the update of a game:
[HttpPut("{Id}")]
public bool UpdateGame(Guid Id, [FromBody] Game game)
{
Game? oldGame = _unitOfWork.Games.GetById(Id);
if (oldGame != null)
{
oldGame.SeasonId = game.SeasonId;
oldGame.VenueId = game.VenueId;
oldGame.GameTypeId = game.GameTypeId;
oldGame.GameTitle = game.GameTitle;
oldGame.GameDateTime = game.GameDateTime;
oldGame.PublishResults = game.PublishResults;
oldGame.GameDetails = game.GameDetails;
oldGame.Buyin = game.Buyin;
oldGame.Fee = game.Fee;
_unitOfWork.Save();
return true;
}
else
{
return false;
}
}
I have tests against the repo and the controller and the update works fine.
I'm now implementing some CRUD pages using Blazor Server:
async Task OnSubmit()
{
if (Game != null)
{
try
{
var response = await _apiClient.httpClient.PutAsJsonAsync<Game>($"/api/Games/{Id}", Game);
bool updated = await response.Content.ReadFromJsonAsync<bool>();
if (updated)
{
_navManager.NavigateTo("/settings/games");
}
}
catch (Exception ex)
{
AlertIsVisible = true;
Message = ex.Message;
MessageType = AlertMessageType.Danger;
}
}
}
This is failing because of a bad request 400, but this is all the output is telling me. The put method in the controller doesn't hit as my breakpoint doesn't trigger.
Simpler models in my app post just fine using similar code.
The difference here I believe is this Game entity:
public class Game
{
public Guid Id { get; set; }
public Guid? SeasonId { get; set; }
public Guid? GameTypeId { get; set; }
public Guid? VenueId { get; set; }
public string? GameTitle { get; set; }
public DateTime GameDateTime { get; set; }
public bool PublishResults { get; set; }
public string? GameDetails { get; set; }
public double Buyin { get; set; }
public double Fee { get; set; }
public virtual Season Season { get; set; } = default!;
public virtual GameType GameType { get; set; } = default!;
public virtual Venue Venue { get; set; } = default!;
public virtual ICollection<Result> Results { get; set; } = new HashSet<Result>();
}
I think there is a serialisation issue with the navigation properties (Season, GameType and Venue - not the Results collection). If I remove the navigation properties the update succeeds. I'm at a loss at this point on how to handle the put request so that it works with the navigation properties on my model.
You declare Season, GameType and Venue as not Nullable [no ?], and then forcibly set to default!, which for any object is null.
You normally use default! when you know an object will be set to a value before any attempt is made to use it, which is not the case here.

.NET List not saved to db?

I want an item to have several sales linked to it:
Sale.cs
public class Sale
{
public int Id { get; set; }
public int Amount { get; set; }
public decimal Price { get; set; }
}
Item.cs
public class Item
{
[Key]
public string Name { get; set; }
public string Market { get; set; }
public string Market_api { get; set; }
public List<Sale> Sales { get; set; }
}
I save Sales like this:
[HttpPost]
public async Task<IActionResult> Sale(SaleViewModel vm, string name)
{
Item item = _repo.GetItem(name);
item.Sales = item.Sales ?? new List<Sale>();
item.Sales.Add(new Sale
{
Amount = vm.Amount,
Price = vm.Price
});
_repo.UpdateItem(item);
await _repo.SaveChangesAsync();
return RedirectToAction("Index");
}
_repo:
public void UpdateItem(Item item)
{
_ctx.Items.Update(item);
}
public async Task<bool> SaveChangesAsync()
{
if(await _ctx.SaveChangesAsync() > 0)
{
return true;
}
return false;
}
and when I debug this it all looks good (List is in item)
Debug information
Peek into db
but when I try to access it like:
item.Sales it always returns null. I honestly don't know what's going on, I can see that the correct foreign key is saved in the Sales table but as to why I can't access it I have no clue.
Include Sales in your repo get items method
_repo.GetItem(name);
_ctx.Items.Include(i => i.Sales).FirstOrDefault(i => i.Name == name);

Update with remove and create entity property collection using Entity Framework Core in web api

I have a models that looks like:
public class Rack
{
public int Id { get; set; }
public int RackRow { get; set; }
public int RackSize { get; set; }
public int SystemId { get; set; }
public System System { get; set; }
public ICollection<Module> Modules { get; set; }
}
public class Module
{
public int Id { get; set; }
public byte ModuleSlot { get; set; }
public string ModuleName { get; set; }
public int RackId { get; set; }
public Rack Rack { get; set; }
}
Those are connected by realtions in fluentAPI like:
modelBuilder.Entity<Entities.Module>().HasOne(entity => entity.Rack).WithMany(entity => entity.Modules).HasForeignKey(entity => entity.RackId);
Now I have this simple Update command in WebApi:
[HttpPut("systemId"), ActionName("UpdateRack")]
public async Task<IActionResult> UpdateRack([FromBody]DTO.Rack requestInput, int systemId)
{
try
{
if (systemId == 0 || !requestInput.Id.HasValue) return StatusCode(StatusCodes.Status403Forbidden);
var systemEntity = await this.systemService.GetSystem(systemId);
if (systemEntity == null) return NotFound();
systemEntity.Racks = this.mapper.Map(new List<DTO.Rack>{requestInput}, systemEntity.Racks);
return Ok(await this.systemService.Update(systemEntity));
}
catch (Exception ex)
{
logger.Error(ex);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
Now this works, but i when I remove or add something to Modules collection it is not processed, so my question is, does entity framework core is capable of adding or removing records in update enity collection operation?
EF Core still can do it.
My entity has total 2 before update first record and create new record.
After updating and creating new, total 3 and first record is updated.

Best way to update an object containing a list of objects in Entity Framework

I have the following models in my API:
namespace API.Models
{
public class StudentDetailsViewModel
{
[Key]
public int StudentId { get; set; }
public AddressViewModel Address { get; set; }
public List<CoursesViewModel> Courses { get; set; }
}
public class AddressViewModel
{
public int AddressId { get; set; }
public int StudentId { get; set; }
public string Address { set; set; }
}
public CoursesViewModel
{
public int CourseId { get; set; }
public int StudentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Grade { get; set; }
}
}
I am writing a PUT method for StudentDetailsViewModel. The list in this model could have a number of records removed or added or a number of fields in one of the records updated. For example, grade for one of the courses updated or a course added or dropped.
What is the best approach in updating a model containing an object list like the above? Is it best to delete the entire list and re-add them?
I have the following thus far:
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutStudenDetailsViewModel(StudentDetailsViewModel studentDetailsViewModel)
{
if(!ModelState.IsValid)
return BadRequest(ModelState);
var address = new DataAccess.Address
{
AddressID = studentDetailsViewModel.Address.AddessId,
StudentID = studentDetailsViewModel.Address.StudentId,
Address = studentDetailsViewModel.Address.Address
};
_context.Entry(address).State = EntityState.Modified;
// TODO: This is where the list Course entity needs to be updated
try
{
await _context.SaveChangesAsync();
}
catch(DbUpdateConcurrencyException)
{
if(!AddressViewModelExists(address.AddressID))
return NotFound();
throw;
}
return StatusCode(HttpStatusCode.NoContent);
}
Just an example from MS documentation for EF Core
public static void InsertOrUpdateGraph(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs
.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog); //or 404 response, or custom exception, etc...
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
}
context.SaveChanges();
}

Error when trying to save a new entity with Entity Framework

I have a VS project that uses Entity Framework to store the information into a database.
I have this class:
DIET_DietHeaders:
using System;
using System.Collections.Generic;
public partial class DIET_DietHeaders
{
public DIET_DietHeaders()
{
this.DIET_DietDetails = new HashSet<DIET_DietDetails>();
}
public int ID { get; set; }
public string CodicePersoneFisiche { get; set; }
public System.DateTime Data { get; set; }
public string Name { get; set; }
public virtual ANAG_Assistiti ANAG_Assistiti { get; set; }
public virtual ICollection<DIET_DietDetails> DIET_DietDetails { get; set; }
}
ANAG_Assistiti:
using System;
using System.Collections.Generic;
public partial class ANAG_Assistiti
{
public ANAG_Assistiti()
{
this.DIET_FoodsBlack = new HashSet<DIET_FoodsBlack>();
this.DIET_DietHeaders = new HashSet<DIET_DietHeaders>();
}
public string CodicePersoneFisiche { get; set; }
public string GruppoSanguigno { get; set; }
public string FattoreRH { get; set; }
public virtual ICollection<DIET_FoodsBlack> DIET_FoodsBlack { get; set; }
public virtual ANAG_PersoneFisiche ANAG_PersoneFisiche { get; set; }
public virtual ICollection<DIET_DietHeaders> DIET_DietHeaders { get; set; }
}
}
Now in my controller, I'm building this code to save the new record into the DIET_DietHeaders table when I receive a request from Json.
[Route("Diet/UpdateDiet")]
[HttpPost]
public HttpResponseMessage UpdateDiet(PatientDietDTO upa)
{
try
{
if (upa != null)
{
UserDTO u = new UserDTO(upa.UserName, upa.Password);
RMessage LoginStatus = Login(u);
if (!login)
{
return Request.CreateResponse(HttpStatusCode.OK, LoginStatus);
}
else
{
string patientA = (assistitoExistsByCF(upa.Patient) ? upa.Patient : getCFAssistoByUsername(upa.Patient));
int res = CreateDiet(upa);
if (upa.ID == null)
{
// effettua insert
var diet = new DIET_DietHeaders
{
CodicePersoneFisiche= upa.Patient
,ANAG_Assistiti = db_data.ANAG_Assistiti.First(c => c.CodicePersoneFisiche == upa.Patient)
, Data=upa.Data
,Name=upa.Name
,Calories=upa.Calories
,WaterCount=upa.WaterCount
,CaloriesTarget=upa.CaloriesTarget
,ProteinTarget=upa.ProteinTarget
,FatTarget=upa.Fat
};
diet.CodicePersoneFisiche = "CFPALUMBO22";
db_data.DIET_DietHeaders.Add(diet);
db_data.SaveChanges();
log.Debug("save done");
int id = diet.ID;
}
return Request.CreateResponse(HttpStatusCode.Created, (new RMessage((short)status_code.Success, "Diet created")));
}
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, (new RMessage((short)status_code.Failure, "Not well formed JSON")));
}
}
catch (Exception e)
{
e = e.GetBaseException();
log.Error(string.Format("{0} {1}", e.Message, e.StackTrace));
return Request.CreateResponse(HttpStatusCode.InternalServerError, new RMessage((short)status_code.Exception, e.Message));
}
}
If I try to execute a request I get this strange error:
Cannot to insert null into column CodiceFiscaleAssistito of table DIET_DietHeaders
but this field is correctly populated.
Where is my error?

Categories