Say I have two entities like so:
public class Response
{
public int Id { get; set; }
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
public string Text { get; set; }
}
public class Patient
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Response> Responses { get; set; }
}
I want to be able to call
Patient.Responses.Remove(someResponse);
And have entity delete not only the relationship but the Response entity as well. At present if I just delete the relationship I get the following error:
System.InvalidOperationException: 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.
Reading this blog post http://blogs.msdn.com/b/dsimmons/archive/2010/01/31/deleting-foreign-key-relationships-in-ef4.aspx I realised I can achieve this by having the following mapping:
modelBuilder.Entity<Response>().HasKey(m => new { m.Id, m.PatientId });
But I don't want to change my primary key. What I want to do is override DbContext.SaveChanges() and mark for deletion any Responses where the Patient relationship has been deleted. I tried this:
public override int SaveChanges()
{
// Need to manually delete all responses that have been removed from the patient, otherwise they'll be orphaned.
var orphanedResponses = ChangeTracker.Entries().Where(
e => e.State == EntityState.Modified &&
e.Entity is Response &&
e.Reference("Patient").CurrentValue == null);
foreach (var orphanedResponse in orphanedResponses)
{
Responses.Remove(orphanedResponse.Entity as Response);
}
return base.SaveChanges();
}
But I found it's possible to attach a Response with only Response.PatientId set and not Response.Patient, entity wont have loaded the Response.Patient property so my code thinks it's been orphaned and should be deleted.
In summary
What I want to know is how can I can tell that an entity has been modified because it's FK relationship has been removed.
Use this instead:
public override int SaveChanges()
{
var responses = Responses.Local.Where(r => r.Patient == null);
foreach (var response in responses.ToList())
{
Responses.Remove(response);
}
return base.SaveChanges();
}
You need to configure the mappings such that a cascade delete will occur. To do that you need to map the model with WillCascadeOnDelete to true.
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Patient>()
.HasMany(patient=> patient.Responses)
.WithRequired(response => response.Patient)
.HasForeignKey(response => response.PatientId)
.WillCascadeOnDelete(true);
}
}
I think my problem is not with the code but rather with how I assume entity's Attach() method works. I assumed that if I attach a response with PatientId set but not Patient property then entity would populate the Patient property for me.
In fact what I think happens is entity attaches it as it is, then if I mark that entity as modified and save it, entity sees the null Patient property and assumes I want to remove the relationship, so throws an error because it would be orphaned (can't null Response.PatientId). So perhaps everything is working as designed and my SaveChanges() solution works.
Related
I am running into an interesting issue when trying to add an entity to the database with Entity Framework. When I try to add a new exception to the database, I run into the following error:
"Cannot insert explicit value for identity column in table 'Notification' when identity_insert is set to off"
I am guessing this error is caused due to the Notification entity already having an Identifier (Id property). However, my goal is not to store the Notification entity. It just so happens that my NotificationException entity has a reference to an existing Notification entity.
How can I update my NotificationException entities properly without running into this problem? Actually turning the identity_insert off does not seem like a viable solution.
My two model classes:
public class Notification
{
// Primary Key
public long Id { get; set; }
// Properties
public bool IsSent { get; set; }
public bool IsExpired { get; set; }
public int RetryCount { get; set; }
public int RetryTime { get; set; }
}
public class NotificationException
{
// Primary Key
public long Id { get; set; }
// Properties
public int Timestamp { get; set; }
public string Exception { get; set; }
// Foreign Keys
public long NotificationId { get; set; }
// Navigation Properties
public virtual Notification Notification { get; set; }
}
Entity Configuration with Fluent API:
private void ConfigureNotificationEntity(EntityTypeBuilder<Notification> builder)
{
builder.ToTable("Notifications");
builder.HasKey(i => i.Id);
builder.Property(i => i.Id)
.IsRequired()
.ValueGeneratedOnAdd();
builder.HasMany(n => n.Exceptions)
.WithOne(e => e.Notification)
.OnDelete(DeleteBehavior.Cascade);
}
private void ConfigureNotificationExceptionEntity(EntityTypeBuilder<NotificationException> builder)
{
builder.ToTable("NotificationExceptions");
builder.HasKey(i => i.Id);
builder.Property(i => i.Id)
.IsRequired()
.ValueGeneratedOnAdd();
builder.HasOne(i => i.Notification)
.WithMany(j => j.Exceptions);
}
The main problem:
public async Task<NotificationException> Add (NotificationException item)
{
_context.NotificationExceptions.Add(item);
await _context.SaveChangesAsync();
return item;
}
As soon as the _context.SaveChangesAsync(); is called, the error mentioned above is thrown.
///Edit
I tested this issue with different objects as well. If the entity has no nested entities, then storing them works just fine. The issue is quite likely with the already known ID of the nested entity.
So it turns out in 2016, EF Core changed the behaviour of the Attach method from EF6: https://github.com/dotnet/efcore/issues/4424
to wit:
Add: Adds every reachable entity that is not already tracked
Attach: Attaches every reachable entity, except where a reachable entity has a store generated key and no key value is assigned, these will be marked as added.
Therefore, the solution is just to change
_context.NotificationExceptions.Add(item); to
_context.NotificationExceptions.Attach(item);
This will Add the new NotificationException pushed without a key; however the child Notification, with a declared key, will be attached as 'Unchanged'.
The problem looks like an issue with how you add notification object to your notificationException object.
Change your Add function like so:
public async Task<NotificationException> Add (NotificationException item)
{
var notification = _context.Notifications.Find( x => x.Id == yourNotificationId);
item.Notification = notification;
_context.NotificationExceptions.Add(item);
await _context.SaveChangesAsync();
return item;
}
The trick here is we got notification entity from same context that we will add notificationException object.
Check this link for more info
Found a fix!
var exception = new NotificationException()
{
Exception = "Very serious exception!",
QueuedNotificationId = notification.Id,// <-- setting the foreign key
Timestamp = 420
};
Instead of referencing the entire Notification object within the Exception entity, I just set the foreign key. This way I can store the entity just fine. Still a bit silly how EF doesn't automatically recognize the Notification entity already exists.
I use Entity framework 6 in my projects and I always have doubts regarding some of the concepts which are used to delete objects using EF.
I still don't know which one works in which scenario. I just try all and if one works I leave it until the code is working. But no wi need to understand this concept once and for all. I did my research my unable to understand the concept clearly.
I have a domain class in EF which have multiple referencing entities. For example. I have a domain class called Course and It has multiple referencing objects mentioned below in the code.
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual PricingSchedule PricingSchedule { get; set; }
public virtual ICollection<CustomerCourse> AssignedCustomers { get; set; }
public virtual ICollection<License> Licenses { get; set; }
public virtual ICollection<GroupLicense> GroupLicenses { get; set; }
public virtual ICollection<GroupCourse> GroupCourses { get; set; }
public virtual ICollection<Learner> Learners { get; set; }
}
Now I have to delete the course from the DB with all of its referencing entities. For example, If the course is deleting then its properties like AssignedCustomers, Licenses etc all must be deleted.
But I don't understand one thing using Entity framework.
For deleting an entity from DB we have multiple options like.
Remove
RemoveRange
EntityState.Deleted
Sometimes Remove works but sometime RemoveRange Works and sometime Entitystate.Deleted works. Why?
My code is for deleting a Course
var courses = _context.Courses
.Include("AssignedCustomers")
.Include("PricingSchedule")
.Include("Licenses")
.Include("GroupCourses")
.Include("GroupLicenses")
.Where(e => courseIds.Contains(e.Id)).ToList();
if (courses != null && courses.Count > 0)
{
courses.ForEach(currentCourse =>
{
_context.Entry(currentCourse.PricingSchedule).State = EntityState.Deleted;
Sometime remove range works and code run successfully
_context.CustomerCourses.RemoveRange(currentCourse.AssignedCustomers);
Below line of code gives me error but in other scenario it works why?
//currentCourse.AssignedCustomers.ToList().ForEach(ac =>
//{
// //currentCourse.AssignedCustomers.Remove(ac);
// _context.Entry(ac).State = EntityState.Deleted;
//});
_context.Entry(currentCourse).State = EntityState.Deleted;
});
}
_context.SaveChanges();
Can anyone explain to me the difference in which situation I should use what?
The error I receive most of the time is
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.
This error comes up when I use this piece of code
currentCourse.AssignedCustomers.ToList().ForEach(ac =>
{
_context.Entry(ac).State = EntityState.Deleted;
});
OR
currentCourse.AssignedCustomers.ToList().ForEach(ac =>
{
currentCourse.AssignedCustomers.Remove(ac):
});
after that when I hit SaveChanges The error comes up.
You need to set up the cascade rules in your schema and within Entity Framework so that it knows which related entities will be deleted when you go to delete a course. For instance you will want to cascade delete while others like Learner would likely have a null-able key which can be cleared if a course is removed.
Provided it is set up correctly, you should just need to use: context.Courses.Remove(course); and the related entities will be removed or disassociated automatically. Start with a simpler example of your parent-child relationships, one child to cascade delete, another to disassociate with a nullable FK. Your current example looks to also have many-to-many associations (GroupCourses) so depending on the mapping/relationships the approach will vary.
While I am trying to delete my entity I am getting following error in my function;
A relationship from the 'ProjectWebsiteTag_ProjectUser' AssociationSet is
in the 'Deleted' state.
Given multiplicity constraints, a corresponding 'ProjectWebsiteTag_ProjectUser_Target' must also in the 'Deleted' state.
Here is my code to delete;
public bool Delete(int id)
{
try
{
using (ProjectDataContext context = new ProjectDataContext())
{
ProjectWebsiteTag websiteTag = context.WebsiteTags.FirstOrDefault(p => p.WebsiteTagId == id);
context.WebsiteTags.Remove(websiteTag);
int saveChanges = context.SaveChanges();
return saveChanges > 0;
}
}
catch (DbEntityValidationException e)
{
FormattedDbEntityValidationException newException = new FormattedDbEntityValidationException(e);
throw newException;
}
}
Here is my data class;
public class ProjectWebsiteTag
{
public int WebsiteTagId { get; set; }
public ProjectUser ProjectUser { get; set; }
public ProjectWebsite ProjectWebsite { get; set; }
}
My Config Class;
public ProjectWebsiteTagConfiguration()
{
ToTable("ProjectWebsiteTags");
HasKey(p => p.WebsiteTagId);
HasRequired(p => p.ProjectUser).WithRequiredDependent().WillCascadeOnDelete(false);
HasRequired(p => p.ProjectWebsite).WithRequiredDependent().WillCascadeOnDelete(false);
}
It looks like it is trying to delete User record, but I do not want that.
I just want to delete "ProjectWebsiteTag" and that's it.
What I am missing here?
It isn't trying to delete the ProjectUser. It's trying to insert it. ProjectWebsiteTagConfiguration() is saying that the ProjectWebsiteTags table has a ProjectUser foreign key in it. When you call
ProjectWebsiteTag websiteTag = context.WebsiteTags.FirstOrDefault(p => p.WebsiteTagId == id)
websiteTag has a ProjectUser with an empty string for its UserId property. So either the record in the ProjectWebsiteTags table has an empty string for the foreign key, or EF is newing a ProjectUser (with an empty UserId) when you get from the context. Either way, EF isn't aware of the existence of a ProjectUser with an empty string id, so when you call SaveChanges() it tries to add it. It can't because the UserId field is required and empty.
I had to fix my problem by adding one-to-many relationship configuration in my User class.
It comes to a resolution of I need to add relationship hint on both sides of Configuration declarations.
public class ProjectUserConfiguration : EntityTypeConfiguration<ProjectUser>
{
public ProjectUserConfiguration()
{
ToTable("ProjectUsers");
HasKey(p => p.UserId);
HasMany(p=>p.ProjectWebsiteTags).WithRequired(q=>q.ProjectUser).WillCascadeOnDelete(false);
}
}
I'm trying to set up an many to many relationship with groups and students, so students can be assigned to many groups and groups can have many students assigned to it. I keep getting an error when I go to call context.savechanges(). In my objects I do have proper configuration, virtual ICollection in both. My configuration is as follows:
public class DataContext : DbContext
{
public DbSet<Student> StudentsContext { get; set; }
public DbSet<Group> GroupsContext { get; set; }
public DbSet<Phase> PhaseContext { get; set; }
public DbSet<Admin> AdminContext { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Student>()
.HasMany(g => g.Groups)
.WithMany(s => s.GroupMembers)
.Map(x => x.MapLeftKey("StudentId")
.MapRightKey("GroupId")
.ToTable("Student_XRef_Group"));
}
}
Then in controller just as a test I would try:
var phase = phaseRepository.Select().SingleOrDefault(x => x.PhaseId == phaseId);
phase.Groups.Clear();
//Testing
Group testGroup = new Group();
testGroup.GroupNumber = 1;
testGroup.GroupMembers.Add(AllStudents[0]); //Students of type Student
phase.Groups.Add(testGroup);
//Testing
context.SaveChanges();
Then when it reaches context.savechanges I get the following error:
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.
** SOLVED **
Turns out that by me calling phase.Groups.clear() was the problem, why I do not know. I was hoping maybe can now tell me why?
Calling Clear() on your collection in this case only attempts to detach the relationship between the Phase and the Group and not actually delete the objects. You're therefore attempting to set the foreign key reference for each one to null, hence the non-nullable exception.
I can understand where the confusion comes from, after all we can use phase.Groups.Add(...) to add a new entity but it's important to remember that it does not work the other way around for Remove() and Clear().
To achieve what you want, you can use DeleteObject() on each Group to want to delete:
context.Groups.DeleteObject(groupToDelete);
For an application using Code First EF 5 beta I have:
public class ParentObject
{
public int Id {get; set;}
public virtual List<ChildObject> ChildObjects {get; set;}
//Other members
}
and
public class ChildObject
{
public int Id {get; set;}
public int ParentObjectId {get; set;}
//Other members
}
The relevant CRUD operations are performed by repositories, where necessary.
In
OnModelCreating(DbModelBuilder modelBuilder)
I have set them up:
modelBuilder.Entity<ParentObject>().HasMany(p => p.ChildObjects)
.WithOptional()
.HasForeignKey(c => c.ParentObjectId)
.WillCascadeOnDelete();
So if a ParentObject is deleted, its ChildObjects are too.
However, if I run:
parentObject.ChildObjects.Clear();
_parentObjectRepository.SaveChanges(); //this repository uses the context
I get the exception:
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.
This makes sense as the definition of the entities includes the foreign key constraint which is being broken.
Can I configure the entity to "clear itself up" when it gets orphaned or must I manually remove these ChildObjects from the context (in this case using a ChildObjectRepository).
It is actually supported but only when you use Identifying relation. It works with code first as well. You just need to define complex key for your ChildObject containing both Id and ParentObjectId:
modelBuilder.Entity<ChildObject>()
.HasKey(c => new {c.Id, c.ParentObjectId});
Because defining such key will remove default convention for auto incremented Id you must redefine it manually:
modelBuilder.Entity<ChildObject>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Now calling to parentObject.ChildObjects.Clear() deletes dependent objects.
Btw. your relation mapping should use WithRequired to follow your real classes because if FK is not nullable, it is not optional:
modelBuilder.Entity<ParentObject>().HasMany(p => p.ChildObjects)
.WithRequired()
.HasForeignKey(c => c.ParentObjectId)
.WillCascadeOnDelete();
Update:
I found a way that doesn't need to add navigational properties from the child to the parent entity or to set up a complex key.
It's based on this article which uses the ObjectStateManager to find the deleted entities.
With a list ObjectStateEntry in hand, we can find a pair of EntityKey from each, which represents the relationship that was deleted.
At this point, I couldn't find any indication of which one had to be deleted. And contrary to the article's example, simply picking the second one would get the parent deleted in cases where the child had a navigation property back to the parent. So, in order to fix that, I track which types should be handled with the class OrphansToHandle.
The Model:
public class ParentObject
{
public int Id { get; set; }
public virtual ICollection<ChildObject> ChildObjects { get; set; }
public ParentObject()
{
ChildObjects = new List<ChildObject>();
}
}
public class ChildObject
{
public int Id { get; set; }
}
The other classes:
public class MyContext : DbContext
{
private readonly OrphansToHandle OrphansToHandle;
public DbSet<ParentObject> ParentObject { get; set; }
public MyContext()
{
OrphansToHandle = new OrphansToHandle();
OrphansToHandle.Add<ChildObject, ParentObject>();
}
public override int SaveChanges()
{
HandleOrphans();
return base.SaveChanges();
}
private void HandleOrphans()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
objectContext.DetectChanges();
var deletedThings = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).ToList();
foreach (var deletedThing in deletedThings)
{
if (deletedThing.IsRelationship)
{
var entityToDelete = IdentifyEntityToDelete(objectContext, deletedThing);
if (entityToDelete != null)
{
objectContext.DeleteObject(entityToDelete);
}
}
}
}
private object IdentifyEntityToDelete(ObjectContext objectContext, ObjectStateEntry deletedThing)
{
// The order is not guaranteed, we have to find which one has to be deleted
var entityKeyOne = objectContext.GetObjectByKey((EntityKey)deletedThing.OriginalValues[0]);
var entityKeyTwo = objectContext.GetObjectByKey((EntityKey)deletedThing.OriginalValues[1]);
foreach (var item in OrphansToHandle.List)
{
if (IsInstanceOf(entityKeyOne, item.ChildToDelete) && IsInstanceOf(entityKeyTwo, item.Parent))
{
return entityKeyOne;
}
if (IsInstanceOf(entityKeyOne, item.Parent) && IsInstanceOf(entityKeyTwo, item.ChildToDelete))
{
return entityKeyTwo;
}
}
return null;
}
private bool IsInstanceOf(object obj, Type type)
{
// Sometimes it's a plain class, sometimes it's a DynamicProxy, we check for both.
return
type == obj.GetType() ||
(
obj.GetType().Namespace == "System.Data.Entity.DynamicProxies" &&
type == obj.GetType().BaseType
);
}
}
public class OrphansToHandle
{
public IList<EntityPairDto> List { get; private set; }
public OrphansToHandle()
{
List = new List<EntityPairDto>();
}
public void Add<TChildObjectToDelete, TParentObject>()
{
List.Add(new EntityPairDto() { ChildToDelete = typeof(TChildObjectToDelete), Parent = typeof(TParentObject) });
}
}
public class EntityPairDto
{
public Type ChildToDelete { get; set; }
public Type Parent { get; set; }
}
Original Answer
To solve this problem without setting up a complex key, you can override the SaveChanges of your DbContext, but then use ChangeTracker to avoid accessing the database in order to find orphan objects.
First add a navigation property to the ChildObject (you can keep int ParentObjectId property if you want, it works either way):
public class ParentObject
{
public int Id { get; set; }
public virtual List<ChildObject> ChildObjects { get; set; }
}
public class ChildObject
{
public int Id { get; set; }
public virtual ParentObject ParentObject { get; set; }
}
Then look for orphan objects using ChangeTracker:
public class MyContext : DbContext
{
//...
public override int SaveChanges()
{
HandleOrphans();
return base.SaveChanges();
}
private void HandleOrphans()
{
var orphanedEntities =
ChangeTracker.Entries()
.Where(x => x.Entity.GetType().BaseType == typeof(ChildObject))
.Select(x => ((ChildObject)x.Entity))
.Where(x => x.ParentObject == null)
.ToList();
Set<ChildObject>().RemoveRange(orphanedEntities);
}
}
Your configuration becomes:
modelBuilder.Entity<ParentObject>().HasMany(p => p.ChildObjects)
.WithRequired(c => c.ParentObject)
.WillCascadeOnDelete();
I did a simple speed test iterating 10.000 times. With HandleOrphans() enabled it took 1:01.443 min to complete, with it disabled it was 0:59.326 min (both are an average of three runs). Test code below.
using (var context = new MyContext())
{
var parentObject = context.ParentObject.Find(1);
parentObject.ChildObjects.Add(new ChildObject());
context.SaveChanges();
}
using (var context = new MyContext())
{
var parentObject = context.ParentObject.Find(1);
parentObject.ChildObjects.Clear();
context.SaveChanges();
}
Want to share another .net ef core solution that worked for me, may be somebody will find it usefull.
I had a child table with two foreign keys (either or), so the accepted solution didn't work for me. Based on the answer by Marcos Dimitrio I came up with the following:
In my custom DbContext:
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
var modifiedEntities = this.ChangeTracker.Entries().Where(c => c.State == EntityState.Modified);
foreach (var entityEntry in modifiedEntities)
{
if (entityEntry.Entity is ChildObject)
{
var fkProperty = entityEntry.Property(nameof(ChildObject.ParentObjectId));
if (fkProperty.IsModified && fkProperty.CurrentValue == null && fkProperty.OriginalValue != null)
{
// Checked if FK was set to NULL
entityEntry.State = EntityState.Deleted;
}
}
}
return await base.SaveChangesAsync(cancellationToken);
}
In EF Core, it can be done by Delete Orphans.
Like this:
dbContext.Children.Clear();
This is my generic solution for Entity Framework 6.4.4, without knowledge of the particular schema.
Note that I start my search for orphan entities from modified entity entries, as in my case I could not find anything searching for deleted relationship entries like other answers suggest.
The logic behind the approach is that an entity removed from a collection of a required relationship will have its foreign key updated to null by the Entity Framework. So we search for all modified entities which have at least one relationship to an end with multiplicity 'One' but having the foreign key set to null.
Add this method to your DbContext subclass. You could override the SaveChanges / SaveChangesAsync methods to call this method automatically.
public void DeleteOrphanEntries()
{
this.ChangeTracker.DetectChanges();
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var orphanEntityEntries =
from entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)
where !entry.IsRelationship
let relationshipManager = entry.RelationshipManager
let orphanRelatedEnds = from relatedEnd in relationshipManager.GetAllRelatedEnds().OfType<EntityReference>()
where relatedEnd.EntityKey == null // No foreign key...
let associationSet = (AssociationSet)relatedEnd.RelationshipSet
let associationEndMembers = from associationSetEnd in associationSet.AssociationSetEnds
where associationSetEnd.EntitySet != entry.EntitySet // ... not the end pointing to the entry
select associationSetEnd.CorrespondingAssociationEndMember
where associationEndMembers.Any(e => e.RelationshipMultiplicity == RelationshipMultiplicity.One) // ..but foreign key required.
select relatedEnd
where orphanRelatedEnds.Any()
select entry;
foreach (var orphanEntityEntry in orphanEntityEntries)
{
orphanEntityEntry.Delete();
}
}
Yes. The following works in EF Core:
Make sure you set the cascade behavior to Cascade like so:
entity.HasOne(d => d.Parent)
.WithMany(p => p.Children)
.HasForeignKey(d => d.ParentId)
.OnDelete(DeleteBehavior.Cascade);
Then set the Parent property to be null in all the child entities that are to be deleted like so:
var childrenToBeRemoved = parent.Children.Where(filter);
foreach(var child in childrenToBeRemoved)
{
child.Parent = null;
}
Now, context.SaveAsync() should delete all the orphaned children entities.
This is not something that is supported automatically by EF right now. You can do it by overriding SaveChanges in your context and manually deleting an child objects that no longer have a parent. The code would be something like this:
public override int SaveChanges()
{
foreach (var bar in Bars.Local.ToList())
{
if (bar.Foo == null)
{
Bars.Remove(bar);
}
}
return base.SaveChanges();
}