I have classes that are structured like the following:
public class Forecast
{
[Key]
[ForeignKey("Stop")]
public string Abbreviation { get; set; }
public virtual Stop Stop { get; set; }
public virtual List<Direction> Directions { get; set; }
}
public class Direction
{
public int DirectionId { get; set;}
public string Abbreviation { get; set;}
public virtual Forecast Forecast { get; set;}
public virtual List<Transport> Transports { get; set;}
}
public class Transport
{
public int TransportId { get; set; }
public int DirectionId { get; set;}
public virtual Direction Direction { get; set;}
}
public partial class Stop
{
[Key]
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
}
I developed these classes and used EF Code First 4.1 to generate the database. CF does appear to properly create all of the primary and foreign key relationships between the classes within the database (MSSQL).
My problem is when I want to delete a Forecast. I thought I do could something like the following:
using (MyContext ctxt = new MyContext())
{
// get a forecast, somehow, not really important
// The one assumption is I'm absolutely sure it's
// Abbreviation key already exists in the database
// and the list of Forecasts.
Forecast f;
ctxt.Forecasts.Remove(f);
}
This deletes the top-level object from the database just fine. However, all of its child objects – all of the directions and transports – remain in the database and become orphaned (their key relationship column gets set to null. I expect that but I DON’T know why they’re not just deleted). I have resorted to recursing down the object graph and calling Remove on every object from its appropriate DbSet in ctxt, but that seems like… the wrong way to do it.
What am I missing here?
Why can’t I just say
ctxt.Forecasts.Remove(f);
and be done with it?
Edit:
@Ladislav gave me the right answer – I
needed to add [Required] to the
Abbreviation property on Direction.However, I am still forced to actually
load the child entities for this to
work – doing something as simple asDirection d = f.Directions[0];
will cause the delete to actually
delete the child entities. I’m well
aware that this is due to lazy
loading. I thought the point of the
FK relationship and ON CASCADE DELETE
was that you wouldn’t have to actually
load the entities to delete them?
Again I seem to be missing something simple.
@Eranga is right that this is done by
ON DELETE CASCADEsetting on relation in the database BUT you are using code first approach and EF creates database for you so the problem here is that your model is not correctly defined because EF didn’t create cascading rule for you.Why? Because of this:
Abbreviationis FK property and it is nullable! So EF looks at your model and it sees that you definedDirectionentity which can haveAbbreviationset to null and because of that it can exists orphaned. Change it to:and removing
Forecastwill delete all relatedDirectioninstances andTransportinstances.Stopis different story because it is parent entity toForecastso it will never be removed withForecast.Edit:
One more point – you don’t want to add
ON DELETE CASCADEto your relations manually because EF have to know about enabled cascade deletes. EF use this information in case where you have related entities loaded.If you place the rule manually into the database you must use fluent mapping and tell EF about this rule as well. Once you force cascade delete in fluent api you don’t need to make it manually in the database – it will be created automatically during database recreation.