简体   繁体   中英

Manually update collections modified status in Entity Framework

I am having some trouble adding or removing relationships from an entity with AutoDetectChangesEnabled = false set. Example models and code are as follows:

public class Category : Entity
{
    public Guid CategoryId { get; set; }        
    public virtual ICollection<UserProfile> UserProfiles { get; set; }     
    public string Name { get; set; }
}

public class UserProfile : Entity
{
    public Guid UserProfileId { get; set; }

    public virtual ICollection<Category> Categories { get; set; }
    public string Name { get; set; }
}

var context = new OfContext();
context.Configuration.AutoDetectChangesEnabled = false;

var userProfile = context.UserProfiles
                         .Include(up => up.Categories)
                         .FirstOrDefault(up => up.UserProfileId == new Guid("XXXX"));
var category = context.Categories
                      .FirstOrDefault(c => c.CategoryId == new Guid("XXXX"));

userProfile.Categories.Add(category);
userProfile.Name = "Updated";
context.Entry(userProfile).State = EntityState.Modified;
context.SaveChanges();

The issue I am having is the addition of the category to the collection is not saved with SaveChanges . If I have AutoDetectChangesEnabled = true , this change is picked up and persisted. So I guess the real question is, how do I manually indicate that this collection has been modified.

I know that for properties, I can use the following

.Property(u => u.Name).IsModified

But I am not seeing anything similar to indicate a collection has been changed.

You can use context.ChangeTracker.DetectChanges() to do it, EF will check if entries are changed, and the mark then to be persited in database, so you just mush change this method before SaveChanges , se the code:

public class Category : Entity
{
    public Guid CategoryId { get; set; }        
    public virtual ICollection<UserProfile> UserProfiles { get; set; }     
    public string Name { get; set; }
}

public class UserProfile : Entity
{
    public Guid UserProfileId { get; set; }

    public virtual ICollection<Category> Categories { get; set; }
    public string Name { get; set; }
}

var context = new OfContext();
context.Configuration.AutoDetectChangesEnabled = false;
var userProfile = context.UserProfiles.Include(up => up.Categories).FirstOrDefault(up => up.UserProfileId == new Guid("XXXX"));
var category = context.Categories.FirstOrDefault(c => c.CategoryId == new Guid("XXXX"));
userProfile.Categories.Add(category);
userProfile.FirstName = "Updated";
context.Entry(userProfile).State = EntityState.Modified;
context.ChangeTracker.DetectChanges();
context.SaveChanges();

In this case, you could also remove that line

context.Entry(userProfile).State = EntityState.Modified;

If you wont use DetectChanges method, you can use ObjectStateManager in this way

var context = new OfContext();
context.Configuration.AutoDetectChangesEnabled = false;
var userProfile = context.UserProfiles.Include(up => up.Categories).FirstOrDefault(up => up.UserProfileId == new Guid("XXXX"));
var category = context.Categories.FirstOrDefault(c => c.CategoryId == new Guid("XXXX"));
userProfile.Categories.Add(category);
userProfile.FirstName = "Updated";
var objectStateManager = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager;
objectStateManager.ChangeRelationshipState(userProfile, category, x => x.Categories, EntityState.Added);
objectStateManager.ChangeObjectState(userProfile, EntityState.Modified);
context.SaveChanges();                    

This line

objectStateManager.ChangeObjectState(userProfile, EntityState.Modified);

Will handle the modification of FirstName property, and any other simple property.

And this line

objectStateManager.ChangeRelationshipState(userProfile, category, x => x.Categories, EntityState.Added);

Will handle the relationship modification of Categories property.

The ChangeTracker class doesn't hold all data about the state of your model. It contains only data about states of the entities but not about the state of independent associations such as many-to-many relationships (as mentioned in this answer )

These changes are tracked in the ObjectStateManager class. You should be able to mark a relationship between two entities as changed with the ChangeRelationshipState method (see official documentation )

Sidenote - to get the ObjectStateManager for a DbContext , it is necessary to cast the DbContext to the ObjectContext

var objectStateManager = 
    ((IObjectContextAdapter)dbcontext).ObjectContext.ObjectStateManager;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM