Reputation: 1651
How can I set a collection to modified in the same way that I would do
_context.Entry(thing).Property(x => x.MyProperty).isModified = true;
like:
_context.Entry(thing).Collection(x => x.MyCollection).isModified = true;
EDIT: The purpose of this, is that my collection is a list of objects stored in a lookup table. I will only have a list of stubs with their id's in this collection and I would like to update the relationships without messing with the audit values and whatever else is contains within the lookup objects. For instance, a contact will have multiple contact types, which for whatever reason are complex objects in this scenario. I want to be able to add and remove types using only the FKs and let EF handle the relationship fixups.
public class Contact
{
public int Id {get;set;}
public list<ContactTypes> ContactTypes {get;set;}
//audit values/other properties
}
public class ContactType
{
public int Id {get;set;}
public string Value {get;set;}
}
Upvotes: 5
Views: 3501
Reputation: 11
You have to synchronize collection properties from detached entities. I wrote the following which I found useful:
namespace DbContextExtensions;
public static class ContextExtensions
{
public static void SyncCollections<T>(this DbContext Context, IEnumerable<T>? Existing, IEnumerable<T>? Detached) where T : class
{
PropertyInfo? key = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(pi => pi.GetCustomAttribute(typeof(KeyAttribute)) != null).FirstOrDefault();
if(key != null)
{ // we have a key property:
if(Detached != null)
{
if(Existing != null)
{
var deleted = Existing.Where(ex => !Detached.Select(d => key.GetValue(d)).Contains(key.GetValue(ex)));
Context.RemoveRange(deleted); // remove any items that don't exist on the Detached collection.
}
Detached.Where(d => ((int)key.GetValue(d)) < 0).All(d => { key.SetValue(d, 0); Context.Set<T>().Add(d); return true; });
}
else
if(Existing != null)
Context.RemoveRange(Existing); // kill them all!
}
}
}
You can use it like this:
var existing = context.MyEntities.Where(mi => mi.Id == TargetId).Include(....
context.SyncCollections(existing.CollectionProp, detached.CollectionProp);
context.MyEntities.Attach(detached);
context.ChangeTracker.Entries().Where(e => e.State == EntityState.Unchanged).All(e => { e.State=EntityState.Modified; return true; });
In this scenario:
I have a lot of collection properties on my entities and this really simplifies the code.
Upvotes: 1
Reputation: 1651
If you have a list of ForeignKey objects, you probably know how frustrating it is to force EF's Relationship Fixup on them. Here's slick way to do that.
public void SetContactTypesToUnchanged(Contact contact)
{
contact.ContactTypes.Each(type => _context.Entry(type).State = EntityState.Unchanged);
_context.SaveChanges();
}
Upvotes: 1
Reputation: 109079
context.Entry
represents a single entity, never a collection. So you have to loop through the collection and mark each entity as modified.
Upvotes: 3