Deleting a entity with a one to many. (DbContext) - asp.net-mvc

The foreach does not delete anything.. it only seems to NULL some fields in the table.. .. and how I delete the main parent after that Im not sure... been looking around for a simple solution with no luck..
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
tMember mem = db.tMembers.Find(id);
// find all VoteScores related to the member and delete them
var voteScores = db.tMemVoteScores.Where(mvs => mvs.MembersID == mem.MembersID).AsEnumerable();
foreach (var mvs in voteScores)
{
var singleMvs = mvs;
mem.tMemVoteScores.Remove(singleMvs);
}
db.SaveChanges();
//Delete the actual member now... where is the remove method?
// mem.remove???
return RedirectToAction("Index");
}

Remove calls in Entity Framework need to be done at the DbSet level, and not on the individual items found in a list. This would also need to be done for the parent object itself, i.e. the following:
foreach (var mvs in voteScores)
{
var singleMvs = mvs;
db.tMemVoteScores.Remove(singleMvs);
}
db.tMembers.Remove(mem);
db.SaveChanges();

tMember mem = db.tMembers.Find(id);
db.Entry(mem).State = EntityState.Deleted;
db.SaveChanges();
How about this?

Related

How to check the original value of an attribute using MVC and EF

Need to compare the original value with the current value in order to do some updates on objects prior to save changes. How to get the original value of an object? The following does not work:
public ActionResult Edit(int id = 0)
{
Project project = db.Projects.Find(id);
...
db.Projects.Attach(project); // ATTACH here
...
}
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
db.Entry(project).State = EntityState.Modified; // The STATE was DETACHED here...
...
}
Is this a configuration issue, or did I miss something? Thanks in advance for any help!
The problem with your solution is the lifetime of the controller. By default a new instance of the controller is generated for each client request. This means that you need to load the original value inside the POST method of edit, because you cannot share the instance of your context.
I have edited the code sample after #Gerard's comment
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
var original_value = db.Projects.Find(project.ProjectId);
or
var original_value_detached = db.Projects.AsNoTracking().Where(P => P.ProjectId == project.ProjectId).FirstOrDefault();
}
}
Or you write your own implementation of the controller factory to share you context between two requests, but you should consider requests from different clients. You can find an example here
It looks like you are implementing concurrency. May be, you can try to catch your changes using DbUpdateConcurrencyException. Something like this:
try
{
...
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var dbValue = (Project)entry.GetDatabaseValues().ToObject();
if(dbValue.State == EntityState.Modified)
{
//***your code
}
}

Store update, insert, or delete statement affected

Im learning MVC 4. I have created a database first project using EF5. In my edit view I want to add a product number to a customer. When I hit save I get the message below. I think it is because product number is null in the product table, hence it cannot update. Can I get around this? I have added my edit control
public ActionResult Edit(int id = 0)
{
UserProfile userprofile = db.UserProfiles.Find(id);
if (userprofile == null)
{
return HttpNotFound();
}
//ViewBag.userId = new SelectList(db.Devices, "DeviceID", "DeviceIMEI", userprofile.UserId);THIS CREATES A NEW ENTRY IN USERPROFILE TABLE
ViewBag.Device_DeviceID = new SelectList(db.Devices, "DeviceID", "DeviceIMEI", userprofile.Device);
ViewBag.ShippingDetails_ShippingDetailsID = new SelectList(db.ShippingDetails, "ShippingDetailsID", "Address1", userprofile.ShippingDetails_ShippingDetailsID);
return View(userprofile);
}
//
// POST: /User/Edit/5
[HttpPost]
public ActionResult Edit(UserProfile userprofile)
{
if (ModelState.IsValid)
{
db.Entry(userprofile).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
//ViewBag.userId = new SelectList(db.Devices, "DeviceID", "DeviceIMEI", userprofile.UserId);
ViewBag.Device_DeviceID = new SelectList(db.Devices, "DeviceID", "DeviceIMEI", userprofile.Device);
ViewBag.ShippingDetails_ShippingDetailsID = new SelectList(db.ShippingDetails, "ShippingDetailsID", "Address1", userprofile.ShippingDetails_ShippingDetailsID);
return View(userprofile);
}
"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"
It looks like you dont pass Id of UserProfile from
view to controller.
You should add
#Html.HiddenFor(model => model.Id)
to your form in view
You're posting a view model, which is disconnected from your entity framework, and trying to tell the EF that it has changed -- which it doesn't know about. Try something like this instead,
var obj = yourContext.UserProfiles.Single(q=>q.Id==userProfile.Id);
obj = userprofile; // ... Map userprofile to the tracked object, obj
yourContext.SaveChanges();
Try this:
if (ModelState.IsValid)
{
db.UserProfiles.Attach(userProfile);
db.SaveChanges();
return RedirectToAction("Index");
}

Adding New Child Object Whilst Modifying Existing Children Entity Framework

I've look at some of the answers to similar questions and they don't really seem to fit mine.
I'm trying to incorporate a pattern from Entity Framework: DbContext(page 90) and it doesn't seem to work. The code that I'm using is given below:
[HttpPost]
public ActionResult Edit(Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.Entry(order).State = EntityState.Modified;
foreach (var orderDetail in order.OrderDetails)
{
if (orderDetail.OrderId == 0)
{
db.Entry(orderDetail).State = EntityState.Added;
}
else
{
db.Entry(orderDetail).State = EntityState.Modified;
}
// The example order that I'm updating has two child entities
// so this orderId will be for the third, added one.
int addedOrderDetailId = order.OrderDetails[2].OrderId;
}
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName", order.CustomerId);
return View(order);
}
I've been running an example where the Order object has two existing OrderDetail objects and I'm attempting to add a third. I included the addedOrderDetailId variable, so that I could add it to the 'Watch' and see when it changed
What I've found is happening is that the OrderId of the added OrderDetail object (which is 0 when the foreach loop is entered) is being updated by entity framework to the OrderId of the Order object. This is happening at after the first iteration through the foreach loop (when the first child entity is having its state changed to modified. This means that all three children are being marked as modified. This is causing SaveChanges() to try to update an entry into the database that doesn't exist.
If anyone else has had this problem, then I would be greatful for any advice as to get around this. I will also have to deal with existing child objects being deleted, but I haven't got around to this yet, so if anyone knows of a pattern for this, that would also be appreciated.
Edit:
After taking Slauma's advice and removing db.Orders.Add(order). I was able to move the call to db.Entry(order).State underneath the foreach loop. This allowed me to loop through the loop and set the state of each OrderDetail object to modified for the existing ones and added for the added one. I then simply had to assign the OrderId of the parent to the OrderId of the child and the update was successful. I've also included the code that I've used to delete child objects during the edit. I'm not sure how efficient this is, but it works. Here is the revised code:
[HttpPost]
public ActionResult Edit(Order order)
{
if (ModelState.IsValid)
{
List<int> previousProductIds = db.OrderDetails
.Where(ep => ep.OrderId == order.OrderId)
.Select(ep => ep.ProductId)
.ToList();
List<int> currentProductIds = order.OrderDetails
.Select(o => o.ProductId)
.ToList();
List<int> deletedProductIds = previousProductIds
.Except(currentProductIds).ToList();
foreach (var deletedProductId in deletedProductIds)
{
OrderDetail deletedOrderDetail = db.OrderDetails
.Where(od => od.OrderId == order.OrderId && od.ProductId == deletedProductId)
.Single();
db.Entry(deletedOrderDetail).State = EntityState.Deleted;
}
foreach (var orderDetail in order.OrderDetails)
{
if (orderDetail.OrderId == 0)
{
db.Entry(orderDetail).State = EntityState.Added;
orderDetail.OrderId = order.OrderId;
}
else
{
db.Entry(orderDetail).State = EntityState.Modified;
}
}
db.Entry(order).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName", order.CustomerId);
return View(order);
}
Remove this line from your code:
db.Orders.Add(order);
This will actually put the order including all orderDetails into Added state. Relationship fixup (which happens automatically in Add) will set the OrderId of all OrderDetails to the key of the order. When you enter the loop orderDetail.OrderId is != 0 for all detail items and you always enter the branch which sets the state to Modified. No orderDetail item is in Added state anymore when the loop is finished.

Entity Framework Debacle

I am developing an MVC application which and I'm not clear on the ideal way to save these entities. What I am doing now works but is nasty to say the least.
My action method takes ViewModels and uses Automapper to map them to the related entities. My entities are Requestor, Order and OrderDetail.
Obviously all these calles to the SaveChanges method are wrong but I have been running into issues of new children being added instead of updated amongst other things.
Any help on how this code should look would really be appreciated.
Jason MacKenzie
[HttpPost]
public ActionResult Edit(FormCollection formValues, RequestorViewModel requestor, OrderViewModel order, List<OrderDetailViewModel> OrderDetails)
{
var query = from r in db.Requestors
where r.RequestorID == requestor.RequestorID
select r;
var req = query.SingleOrDefault();
var orderQuery = from o in db.Orders
where o.RequestorID == requestor.RequestorID
select o;
var or = orderQuery.SingleOrDefault();
List<OrderDetail> orDet = db.OrderDetails.Where(od => od.OrderID == or.OrderID).ToList();
Mapper.CreateMap<RequestorViewModel, Requestor>();
req = Mapper.Map<RequestorViewModel, Requestor>(requestor);
Mapper.CreateMap<OrderViewModel, Order>();
or = Mapper.Map<OrderViewModel, Order>(order);
Mapper.CreateMap<OrderDetailViewModel, OrderDetail>();
orDet = Mapper.Map<List<OrderDetailViewModel>, List<OrderDetail>>(OrderDetails);
foreach (OrderDetail od in orDet)
{
db.OrderDetails.ApplyCurrentValues(od);
db.SaveChanges();
}
db.Requestors.ApplyCurrentValues(req);
db.SaveChanges();
db.Orders.ApplyCurrentValues(or);
db.SaveChanges();
return View("Index");
}
First you can clean up your queries quite a bit.
var req = (from r in db.Requestors
where r.RequestorID == requestor.RequestorID
select r).SingleOrDefault();
var or = (from o in db.Orders.Include("OrderDetails")
where o.RequestorID == requestor.RequestorID
select o).SingleOrDefault();
Take a look at how .Include works to map all your related data. It's very useful.
You should only need to call SaveChanges() once too.

Update Data using Linq2Sql

I have this in my mvc ActionResult
[HttpPost]
public ActionResult _ChangeDetails( [Bind(Prefix="ContactDetails")] userDetail UserDetail )
{
MemberChangeDetailsFormViewModel fvm = new MemberChangeDetailsFormViewModel();
if (ModelState.IsValid)
{
//save
UserDetailRepository repository = new UserDetailRepository();
repository.Save(UserDetail);
return RedirectToAction("Index", "Member");
}
fvm.ContactDetails = UserDetail;
return View(fvm);
}
Then in my repository I have;
if (userDetail.id != Guid.Empty)
{
userDetail orig = dc.userDetails.Where(x => x.id == userDetail.id).Single();
dc.userDetails.Attach(userDetail, orig);
dc.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, userDetail);
dc.SubmitChanges();
}
However the "attach" is generating an error; Cannot add an entity with a key that is already in use.
I understand that it's caused because L2S already has the object attached. I have tried doing this with and without the orig object but get the same message.
What would be the best practice to update the data in the table from the model?
Linq-to-SQL can't deal with two objects with the same key in the same data context. Some options:
Option 1: Use two different data contexts:
public void Update(UserDetail modifiedUser)
{
using (UserDetailDataContext dc1 = new UserDetailDataContext())
using (UserDetailDataContext dc2 = new UserDetailDataContext())
{
UserDetail originalUser = dc1.UserDetails.Single(u => u.id == modifiedUser.id);
dc2.UserDetails.Attach(modifiedUser, originalUser);
dc2.SubmitChanges();
}
}
Option 2: Don't pull the original object; annotate the new object as an update:
public void Update(UserDetail modifiedUser)
{
using (UserDetailDataContext dc = new UserDetailDataContext())
{
dc.UserDetails.Attach(modifiedUser);
dc.Refresh(RefreshMode.KeepCurrentValues, modifiedUser);
dc.SubmitChanges();
}
}
There's more discussion in this question, and Rick Strahl has a blog entry that covers a versioning-based strategy, if you're willing to modify the table schema to add a timestamp.

Resources