How to update foreign key using entitystate.modified? - c#

I am new to MVC3 im trying to perform update function after edit which contents two foreign keys (BRANCH_ID,ITEM_MASTER_ID).
The problem im facing easy when branch_id or Item_master_id are not changed the row gets updated but if the foreigns keys change its throwing me an error:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
Here is my model
public partial class MATERIAL
{
public int ID { get; set; }
public int ITEM_MASTER_ID { get; set; }
public int BRANCH_MASTER_ID { get; set; }
public string NAME { get; set; }
public string ADDRESS_DETAILS { get; set; }
public virtual BRANCH_MASTER BRANCH_MASTER { get; set; }
public virtual ITEM_MASTER ITEM_MASTER { get; set; }
}
My edit function code
[HttpPost]
public ActionResult Edit(MATERIAL material)
{
if (ModelState.IsValid)
{
db.Entry(material).State = EntityState.Modified;
db.SaveChanges();
int tempid = material.ID;
return RedirectToAction("listcontinue", new { id = tempid });
}
return View(material);
}
Help me in perform update even if my foreigns keys are changed.
here is my improved edit code
public ActionResult Edit(MATERIAL material)
{
var temp = Convert.ToString(material.ITEM_NAME);
using (var context = new material_managementEntities())
{
var temp1 = (from cs in context.ITEM_MASTER
where cs.ITEM_NAME == temp
select cs.ID).FirstOrDefault();
material.ITEM_MASTER_ID = temp1;
}
var temp2 = Convert.ToString(material.ITEMGROUP);
using (var context = new material_managementEntities())
{
var temp3 = (from cs in context.ITEM_GROUP_MASTER
where cs.ITEM_GROUP_NAME == temp2
select cs.ID).FirstOrDefault();
material.ITEM_MASTER_ITEM_GROUP_MASTER_ID = temp3;
}
if (ModelState.IsValid)
{
db.MATERIALs.Attach(material);
db.Entry(material).State = EntityState.Modified;
db.SaveChanges();
int tempid = material.ID;
return RedirectToAction("listcontinue", new { id = tempid });
}
return View(material);
}

I think you forgot to attach the object:
db.Materials.Attach(material);
db.Entry(material).State = EntityState.Modified;
db.SaveChanges();

I think in the context EF doesn't know about the foreign keys, I had to use an includes first before I update the foreign key:
db.Entry(team).State = EntityState.Modified;
db.SaveChanges();
var updatedTeam = db.Teams.Include(x=> x.GameType).Where(x=> x.ID == team.ID).Single();
updatedTeam.GameType = db.GameTypes.Find(GameTypeId);
db.SaveChanges();

Related

Problem when Updating value (PUT) in ASP.NET core ef

I'm currently learning ASP.NET Core and having some problem. Basically, I have a model like this:
public class WorkRoom
{
[Key]
public int WorkRoomId { get; set; }
[ForeignKey("Room")]
[Required]
public int RoomId { get; set; }
public Room Room { get; set; }
[ForeignKey("Employee")]
[Required]
public string Id { get; set; }
public virtual ApplicationUser Employee { get; set; }
}
and a Dto for this model cause i only want to pass two value is RoomId and Id(using identityUser).
WorkRoomDto.cs
public class WorkRoomDto
{
public int RoomId { get; set; }
public string Id { get; set; }
}
I can write POST, GET and DELETE method normally as i wish, but I have some problems writing PUT.
public async Task<Response> PutWorkRooms(int id, WorkRoomDto workRoom)
{
var respone = new Response();
var workRoomId = _context.WorkRooms.Any(i => i.WorkRoomId == id);
var userId = await userManager.FindByIdAsync(workRoom.Id);
if (workRoomId)
{
if (userId == null)
{
respone.Status = false;
respone.Message = "User not exist";
return respone;
}
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
_context.Entry(newWorkRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
respone.Status = true;
respone.Message = "Successfully updated.";
respone.Data = workRoom;
}
else
{
respone.Status = false;
respone.Message = "WorkRoomId no exist.";
}
return respone;
}
But it showed Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s).
Before the autoMappter I had tried
_context.Entry(workRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
But it says the Dto is not in the DbContext. How can I fix this?
From your code, WorkRoomId is 0 in newWorkRoom. You need set the value for it. Just change like below:
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
newWorkRoom.WorkRoomId = id; //add this line...
_context.Entry(newWorkRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
Automapper give you a brand new object that created locally (not taking back from the database), therefore, not tracked by the dbContext.
So, when you try to update newWorkRoom by forcing the state to modified, the dbContext know it want to see an update operation, but unfortunately, nothing got tracked, then execute nothing.
Try this
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
if (_context.Entry(newWorkRoom).State == EntityState.Detached)
{
var entityFromDb = await _context.WorkRooms.FindAsync(id);
_context.Entry(entityFromDb).CurrentValues.SetValues(newWorkRoom);
}

How to update data with child using EF Core

I have these two models:
public class Film
{
public long Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Director { get; set; }
public string Synopsis { get; set; }
public int? Release { get; set; }
[Required]
public Genre Genre { get; set; }
}
public class Genre
{
public long Id { get; set; }
public string Description { get; set; }
}
And I want to be able to update a Film's Genre through a PUT method. I am currently trying this, but I get the following error:
[HttpPut]
public async Task<IActionResult> UpdateFilm(Film film)
{
var existingFilm = await _context.Films
.Include(f => f.Genre)
.FirstOrDefaultAsync(f => f.Id == film.Id);
if (existingFilm == null)
{
return NotFound(new JsonResponse { Success = false, Message = "Impossible to update, film was not found", Data = null });
}
existingFilm.Title = film.Title;
existingFilm.Synopsis = film.Synopsis;
existingFilm.Release = film.Release;
existingFilm.Director = film.Director;
if (existingFilm.Genre.Id != film.Genre.Id)
{
existingFilm.Genre.Id = film.Genre.Id;
existingFilm.Genre.Description = film.Genre.Description;
//_context.Entry(existingFilm.Genre).State = EntityState.Modified;
_context.Entry(existingFilm).CurrentValues.SetValues(film);
}
_context.Films.Update(existingFilm);
try
{
await _context.SaveChangesAsync();
}
catch (Exception e)
{
return BadRequest(new JsonResponse { Success = false, Message = e.Message, Data = null });
}
return Ok(new JsonResponse { Success = true, Message = "Film updated with success", Data = film });
}
The error message is:
System.InvalidOperationException: The property 'Id' on entity type 'Genre' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.
Anyone able to help? Thanks a lot.
According to the error, its existingFilm.Genre.Id that you cannot update the id, if its not equal to the id.
My suggestion would be ignore the id update, but if it is necessary:
if (existingFilm.Genre.Id != film.Genre.Id)
{
var genre = _context.Genre.FirstOrDefault(x => x.Id == film.Genre.Id);
// Update the fields needed except the id
existingFilm.Genre = genre;
}

Find method not assigning values to object

I have a web project that everything is working and this below line works for other models except for this one. I'm just needing some info on where to start looking for the solution at.
When I debug it I see that it is getting all the new data that has been edited but it does not assign the new data to EditedtimeEntry. The EditedtimeEntry var has the old data not the new data that was edited. I looked at the timeEntry.Id and it has the new edit its just not being assigned to the EditedtimeEntry. There is no exception or build errors it just does not save the changes, and it looks like the reason it is not save the changes is because the EditedtimeEntry var is not getting the new data assigned to it for some reason. Can anyone point me in the right direction?
TimeEntry EditedtimeEntry = db.TimeEntries.Find(timeEntry.Id);
Here is the Full method with the problem:
public ActionResult Edit( [Bind(Include = "Id,Description,Rate,Paid,Tech,Company")] TimeEntry timeEntry)
{
if (ModelState.IsValid)
{
TimeEntry EditedtimeEntry = db.TimeEntries.Find(timeEntry.Id);
Technician tech = db.Technician.Single(m => m.PhoneNumber == timeEntry.Tech.PhoneNumber);
EditedtimeEntry.Tech = tech;
Company comp = db.Companies.Single(m => m.Name == timeEntry.Company.Name);
EditedtimeEntry.Company = comp;
db.Entry(EditedtimeEntry).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(timeEntry);
}
I have other methods for other models that are identical to this one and it works. Here is alsos the model
public class TimeEntry
{
[Key]
public Guid Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string Description { get; set; }
public Rate Rate { get; set; }
public Technician Tech { get; set; }
public bool Paid { get; set; }
public Company Company { get; set; }
}
public enum Rate { Desktop, Network, Remote, Phone }
Thanks =)
Basically, what you are doing is exactly the contrary of what you want to do :) when you recover the item from the database, you are simply getting the item unchanged that is still in database. What you want is update the database item and then save changes (with EntityState.Modified then SaveChanges())
You simply want to edit timeEntry so that all changes done in the UI are translated into DB :
public ActionResult Edit( [Bind(Include = "Id,Description,Rate,Paid,Tech,Company")] TimeEntry timeEntry)
{
if (ModelState.IsValid)
{
Technician tech = db.Technician.Single(m => m.PhoneNumber == timeEntry.Tech.PhoneNumber);
timeEntry.Tech = tech;
Company comp = db.Companies.Single(m => m.Name == timeEntry.Company.Name);
timeEntry.Company = comp;
db.Entry(timeEntry).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(timeEntry);
}

Create or update if item exist

I have a create form where if the specific Medicine exist, its number of supply will update or added with the new entry however if the specific Medicine doesn't exist, it will create a new batch of data.
Im having trouble at understanding how update works in MVC.
Here is the error:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.
Here is my controller:
public ActionResult Create([Bind(Include = "SupplyID,MedicineID,Expiration,NumberOfSupply")] Supply supply)
{
if (ModelState.IsValid)
{
bool supplyExsist = db.Supplies.Any(x => x.Expiration == supply.Expiration && x.MedicineID == supply.MedicineID);
if (supplyExsist)
{
var currentSupply = (from x in db.Supplies //get current supply
where x.MedicineID == supply.MedicineID
&& x.Expiration == supply.Expiration
select x.NumberOfSupply).First();
db.Entry(supply).State = EntityState.Modified;
supply.NumberOfSupply = currentSupply + supply.NumberOfSupply;
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
db.Supplies.Add(supply);
db.SaveChanges();
return RedirectToAction("Index");
}
}
ViewBag.MedicineID = new SelectList(db.Medicines, "MedicineID", "MedicineName", supply.MedicineID);
return View(supply);
}
Model:
public class Supply
{
[Key]
public int SupplyID { get; set; }
[ForeignKey("Medicine")]
public int MedicineID { get; set; }
public Medicine Medicine { get; set; }
[DataType(DataType.Date)]
public DateTime Expiration { get; set; }
[Display(Name = "Quantity")]
[Range(1, int.MaxValue, ErrorMessage = "The value must be greater than 0")]
public int NumberOfSupply { get; set; }
}
just try this
db.Supplies.AddOrUpdate(h => h.medicineID,supply));
it will check if there is a row with the same medicine ID in db if not it adds a new one else it updates it
You should change your if block with following :
if (supplyExsist)
{
var currentSupply = (from x in db.Supplies //get current supply
where x.MedicineID == supply.MedicineID
&& x.Expiration == supply.Expiration
select x.NumberOfSupply).First();
db.Supplies.Attach(supply);
db.Entry(supply).State = EntityState.Modified;
supply.NumberOfSupply = currentSupply + supply.NumberOfSupply;
db.SaveChanges();
return RedirectToAction("Index");
}

MVC 4 - Entity Framework, ViewModel and Object Context

I'm using Entity Framework with MVC 4 to develop a web application. I'm also using a ViewModel named VehicleTypeViewModel which is created like this :
public class VehicleTypeViewModel
{
public VehicleType VehicleType { get; set; }
public ProductType ProductType { get; set; }
[RegularExpression(#"^[a-zA-Zàéèêçñ\s][a-zA-Zàéèêçñ\s-]+$", ErrorMessage = "Invalid name !")]
public string Name { get; set; }
[Range(0, 300)]
public int CO2 { get; set; }
public List<SelectListItem> ProductCompanies { get; set; }
public List<SelectListItem> MotorTypes { get; set; }
}
In my Edit Action, everything's good but one thing : when I debug, arriving to the db.Attach(...) step, my app throws an exception which says :
The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state.
Here are my post action :
[HttpPost]
public ActionResult Edit(VehicleTypeViewModel vtvm)
{
ViewBag.Id_VehicleMotorType = new SelectList(db.VehicleMotorTypes, "Id_VehicleMotorType", "Name", vtvm.VehicleType.Id_VehicleMotorType);
ViewBag.Id_ProductCompany = new SelectList(db.ProductCompanies, "Id_ProductCompany", "Name", vtvm.ProductType.Id_ProductCompany);
vtvm.ProductCompanies = db.ProductCompanies.ToList().Select(c => new SelectListItem { Text = c.Name, Value = c.Id_ProductCompany.ToString() }).ToList();
vtvm.MotorTypes = db.VehicleMotorTypes.ToList().Select(v => new SelectListItem { Text = v.Name, Value = v.Id_VehicleMotorType.ToString() }).ToList();
VehicleType vehicleType = db.VehicleTypes.Single(v => v.Id_VehicleType == vtvm.VehicleType.Id_VehicleType);
ProductType productType = db.ProductTypes.Single(p => p.Id_ProductType == vtvm.ProductType.Id_ProductType);
VehicleMotorType vehicleMotorType = null;
ModelStateDictionary errors = Validator.isValid(vtvm.ProductType);
if (ModelState.IsValid)
{
if (errors.Count > 0)
{
ModelState.Merge(errors);
return View(vtvm);
}
productType.Model = vtvm.ProductType.Model;
productType.CatalogPrice = vtvm.ProductType.CatalogPrice;
productType.Id_ProductCompany = vtvm.ProductType.Id_ProductCompany;
if (!string.IsNullOrWhiteSpace(vtvm.Name) && (vtvm.CO2 > 0))
{
vehicleMotorType = new VehicleMotorType()
{
CO2 = vtvm.CO2,
Name = vtvm.Name
};
vehicleType.CO2 = vtvm.VehicleType.CO2;
vehicleType.VehicleMotorType = vehicleMotorType;
vehicleType.Id_ProductType = vtvm.ProductType.Id_ProductType;
}
else
{
vehicleType.CO2 = vtvm.VehicleType.CO2;
vehicleType.Id_ProductType = vtvm.ProductType.Id_ProductType;
vehicleType.Id_VehicleMotorType = vtvm.VehicleType.Id_VehicleMotorType;
}
db.VehicleTypes.Attach(vehicleType);
db.ProductTypes.Attach(productType);
db.ObjectStateManager.ChangeObjectState(vehicleType, EntityState.Modified);
db.ObjectStateManager.ChangeObjectState(productType, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vtvm);
}
I have no idea why I'm dealing with this kind of error. Any idea to solve this please?
Traffy,
Short answer, you should remove this 2 lines
db.VehicleTypes.Attach(vehicleType);
db.ProductTypes.Attach(productType);
When you retrieve any entity from an EntityFramework instance and modifies any property the Entity Framework is tracking the changes automatically.
To apply the changes to the database you just need to call
db.SaveChanges();
To understand better when to use Add and Attach methods you should read this
Entity Framework 4 - AddObject vs Attach.
I hope it helps.
Just remove the "Attach" as the object is already in the database, no need to attach again, just alter the state and save changes
var comp = (from com in db.tbl_CompGroup where com.Group_Id == GroupID select com).FirstOrDefault();
comp.Survey_Est = surveyPost.Survey_Est;
//db.tbl_CompGroup.Attach(comp);
db.ObjectStateManager.ChangeObjectState(comp, System.Data.EntityState.Modified);
db.SaveChanges();

Categories