Note that there are some explanatory texts on larger screens.

plurals
  1. POHandling dependent entities when deleting the principal with Entity Framework 5
    primarykey
    data
    text
    <p>Here's the situation in its most simplified form using the EF5 Code-First approach:</p> <pre><code>public abstract class EntityBase&lt;PK&gt; { public PK ID { get; set; } } public class Country : EntityBase&lt;string&gt; { public string Name { get; set; } } public class Address : EntityBase&lt;int&gt; { [Required] public string CountryID { get; set; } public Country Country { get; set; } // ... other address properties ... } </code></pre> <p>The one-to-many relationship between <code>Address</code> and <code>Country</code> is set up with no cascade-delete like so:</p> <pre><code>modelBuilder.Entity&lt;Address&gt;() .HasRequired(a =&gt; a.Country) .WithMany() .HasForeignKey(a =&gt; a.CountryID) .WillCascadeOnDelete(false); </code></pre> <p>Finally, I have a generic base repository class with CRUD methods that call <code>SaveChanges</code> on the underlying DbContext to commit data changes atomically. E.g.:</p> <pre><code>public class EFRepository&lt;T, PK&gt; : IRepository&lt;T, PK&gt; where T : EntityBase&lt;PK&gt; { // // ... other methods ... // public virtual void Delete(T instance) { // ... trigger validations, write to log, etc... _dbContext.Set&lt;T&gt;().Remove(instance); try { _dbContext.SaveChanges(); } catch(Exception ex) { // ... handle the error ... } } } </code></pre> <p><strong>Part 1:</strong></p> <p>Scenario:</p> <pre><code>var countryRepo = new EFRepository&lt;Country&gt;(); var country = countryRepo.Save(new Country() { ID="??", Name="Test Country" }); var addressRepo = new EFRepository&lt;Address&gt;(); var address = addressRepo.Save(new Address() { Country=country }); countryRepo.Delete(country); </code></pre> <p>This should fail due to the existence of a dependent <code>Address</code>. However, afterwards the address ends up with a null in <code>CountryID</code>, which is invalid because <code>Address.CountryID</code> is required, so subsequent <code>SaveChanges</code> calls throw a validation exception unless the address is detached.</p> <p>I expected that when an object is deleted, EF5 will be smart enough to first check for any cascade-delete constraints like the one above and, failing to find any, then proceed to delete the data. But exactly the opposite seems to be the case.</p> <p>Is this a normal behaviour or am I doing something wrong?</p> <p><strong>Part 2:</strong></p> <p>Following a failed <code>SaveChanges</code> call, some <code>Addresses</code> are now in an invalid state in my DbContext and need to be restored to their original values. Of course, I can always do so explicitly for each entity type (<code>Country</code>, <code>State</code>, <code>Order</code>, etc.) by creating specialized repository classes and overriding <code>Delete</code>, but it smells big time. I'd much rather write some general purpose code to gracefully recover related entities after a failed <code>SaveChanges</code> call.</p> <p>It would require interrogating DbContext to get all relationships in which an entity (e.g. <code>Country</code>) is the principal, regardless of whether or not its class defines navigational properties to dependent entities. </p> <p>E.g. <code>Country</code> has no <code>Addresses</code> property, so I need to somehow find in DbContext the definition of the one-to-many relationship between <code>Country</code> and <code>Address</code> and use it to restore all related <code>Addresses</code> to their original values. </p> <p>Is this possible?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload