Reputation: 51
I'm working with an entity framework project where I'm struggling to remove an Item out of one of my collections. I have firstly a "One to Many" relation created between my object "Resto" and "OpeningTimes" the following way:
In my model:
[Table("Resto")]
public class Resto
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; } //TODO Ajouter adresse détaillées
public virtual ICollection<SlotTime> OpeningTimes {get; set;}
public virtual ICollection<ApplicationUser> Administrators { get; set; }
public virtual ICollection<ApplicationUser> Chefs { get; set; }
public virtual Menu Menu {get; set;}
}
[Table("RestoSlotTimes")]
public class SlotTime
{
public int SlotTimeId { get; set; }
public DayOfWeek DayOfWeek { get; set; }
public TimeSpan OpenTime { get; set; }
public TimeSpan CloseTime { get; set; }
public int RestoId { get; set; }
public virtual Resto Resto { get; set; }
}
Since I have other relation from that object (see the one from Applications User) I'm also using Fluent language to remove ambiguities.
The following way:
modelBuilder.Entity<Resto>()
.HasMany<ApplicationUser>(s => s.Administrators)
.WithMany(c => c.Resto_Admin)
.Map(cs =>
{
cs.MapLeftKey("Resto_Admin");
cs.MapRightKey("Admin");
cs.ToTable("RestosAdmins");
});
modelBuilder.Entity<Resto>()
.HasMany<ApplicationUser>(s => s.Chefs)
.WithMany(c => c.Resto_Chefs)
.Map(cs =>
{
cs.MapLeftKey("Resto_Chefs");
cs.MapRightKey("Chef");
cs.ToTable("RestosChefs");
});
modelBuilder.Entity<Resto>()
.HasOptional(s => s.Menu)
.WithRequired(ad => ad.resto)
.WillCascadeOnDelete(true);
But those relations are working fine.
Today I do basic operations on my "OpeningTimes" in my controller as adding a item in the DB with the following:
[HttpPost]
public async Task<ActionResult> AddSlotTimeToRestaurant(AddSlotTimeToRestaurantView model)
{
var resto = await DbManager.Restos.FirstAsync(r => r.Id == model.RestoId);
if(resto != null)
{
if(model.OpenTimeId < model.CloseTimeId)
{
SlotTime slotTime = new SlotTime()
{
DayOfWeek = model.Day,
OpenTime = model.SlotTimeList.timeSpanViews.FirstOrDefault(m => m.Id == model.OpenTimeId).TimeSpan,
CloseTime = model.SlotTimeList.timeSpanViews.FirstOrDefault(m => m.Id == model.CloseTimeId).TimeSpan
};
resto.OpeningTimes.Add(slotTime);
await DbManager.SaveChangesAsync();
return RedirectToAction("edit", new { id = model.RestoId });
}
else
{
ModelState.AddModelError("SelectedSlotTimeId_1_Stop", "L'heure de fermeture doit être après l'heure d'ouverture");
return View();
}
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
This code is working as expected. But now one I try to do another function for removing the object with he following code:
public async Task<ActionResult> RemoveSlotTimeToRestaurant(int RestoId, int SlotId)
{
var resto = await DbManager.Restos.FirstAsync(r => r.Id == RestoId);
if (resto != null)
{
SlotTime slotTime = resto.OpeningTimes.FirstOrDefault(m => m.SlotTimeId == SlotId);
if(slotTime != null)
{
resto.OpeningTimes.Remove(slotTime);
await DbManager.SaveChangesAsync();
return RedirectToAction("edit", new { id = RestoId });
}
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
I have an error on the DbSave line... Where it looks like EF try not to remove my object but just to keep it and set its ID to Null... Which is not what I expect
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted
I have the feeling my relation is well configured for EF. I feel like doing the add and removing relation properly... Do you think this can come from the fluent configuration?
BTW once I remove the "Resto" I have the cascade deleting working well where all my items "OpeningTimes" are well deleted out of the DB without error.
Upvotes: 3
Views: 100
Reputation: 4219
If you want to be able to modify the child entity (OpeningTimes
) through the parent entity (Resto
), then you need to specify the relationship between the two in the model builder:
modelBuilder.Entity<Resto>()
.HasMany(r => r.OpeningTimes)
.WithOptional()
.HasForeignKey(ot => ot.RestoId)
This blog post goes into more detail on handling this scenario.
Another approach would be to just remove the entity directly from the DbSet defined on the DbContext:
DbManager.SlotTimes.Remove(slotTime);
Upvotes: 4
Reputation: 292
When you remove the slotTime, is there another table that has a foreign key related to the OpeningTimes table? It looks as if you are removing the item, and the related table is trying to set the foreign key to null in which that is not allowed. You need to make sure tables related to OpeningTimes are changed appropriately to account for the removal of the record.
Edit: Other answers are more detailed and they're much better than mine.
Upvotes: 0
Reputation: 13965
It looks to me like you need to add another fluent call, to define how the SlotTimes
relate to the Resto
.
From what you're describing, the SlotTime
is entirely dependent on the Resto
: you can't have a SlotTime
that isn't associated with a Resto
. But, evidently EF
can't just infer this: you're going to have to tell it, with fluent configuration.
I can't recreate this from memory because it's too complicated, and I'm not on a machine with VS installed. But I think you'd start with this:
modelBuilder.Entity<Resto>()
.HasMany<TimeSlot>(r => r.OpeningTimes)
.WithOne(t => t.Resto)
....
I apologize for not being able to fill out the whole call for you.
By the way, this other question has a very good answer that explains it all: EF 4: Removing child object from collection does not delete it - why?
Upvotes: 1
Reputation: 682
Can you try like below:
DbManager.Entry(slotTime).State = EntityState.Deleted;
await DbManager.SaveChangesAsync();
Upvotes: 1