2

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.

Lukas Kabrt
  • 5,441
  • 4
  • 43
  • 58
Domtar
  • 33
  • 4

2 Answers2

2

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.

Alberto Monteiro
  • 5,989
  • 2
  • 28
  • 40
  • Thanks for the input, that does seem to work but is there any way to set that state manually (Similar to the Entry().State) so that the call to DetectChanges() is not needed? Presumably it is setting the state of something so that SaveChanges() knows what to update. Again thanks for the info. – Domtar Dec 11 '15 at 22:36
  • @Domtar Why are you running out from DetectChanges? Why do you need do it manually? – Alberto Monteiro Dec 12 '15 at 15:53
  • @Domtar I changed the answer to fit your question, without use DectectChanges. – Alberto Monteiro Dec 12 '15 at 16:02
  • Perfect, thanks for the info. In terms of the AutoDetectChanges, we are importing a large number of relationships and the Repository we are using (genericunitofworkandrepositories.codeplex.com) has us manually setting status for stuff anyway. So Many-Many relationships where the only thing that where not manually set. I figured why go through all the other detect changes code if I was already setting the status for 90% of the changes. Thanks for the great response. – Domtar Dec 14 '15 at 15:48
  • @Domtar Np, sadly that isn't useful either correct answer. – Alberto Monteiro Dec 14 '15 at 16:42
0

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;
Community
  • 1
  • 1
Lukas Kabrt
  • 5,441
  • 4
  • 43
  • 58