I use GraphDiff to update detached object graphs, and I'm getting the above exception when saving a parent and its children.
The models and mapping are:
public class Group
{
public int Id { get; set; }
public virtual ICollection<GroupUser> Users{ get; set; }
}
public class GroupUser
{
public int Id { get; set; }
public int GroupId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class GroupMap : EntityTypeConfiguration<Group>
{
public GroupMap()
{
this.ToTable("groups");
this.HasKey(t => t.Id).Property(t => t.Id).HasColumnName("id").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasMany(t => t.Users)
.WithOptional()
.HasForeignKey(d => d.GroupId)
.WillCascadeOnDelete();
}
}
public class GroupUserMap : EntityTypeConfiguration<GroupUser>
{
public GroupUserMap ()
{
this.ToTable("groups_users");
this.HasKey(t => t.Id).Property(t => t.Id).HasColumnName("id").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(t => t.GroupId).HasColumnName("group_id").IsRequired();
this.Property(t => t.UserId).HasColumnName("user_id").IsRequired();
this.HasRequired(t => t.User)
.WithMany()
.HasForeignKey(d => d.UserId)
.WillCascadeOnDelete(false);
}
}
For inserts and updates I have this following method in my repository:
public override Group Update(Group entity)
{
if (entity.Users != null)
{
entity.Users.ToList()
.ForEach(x =>
{
x.GroupId = x.Id == 0 ? 0 : entity.Id;
x.UserId = x.User.Id;
});
}
return dbContext.UpdateGraph<Group>
(entity,
map => map.OwnedCollection(x => x.Users, with => with.AssociatedEntity(c => c.Users))
);
}
I do the following:
Whilst debugging, the state of the user entities being added is always detached, and both GroupId
and Id
are set to 0 for new entities. The first 2 users added are saved successfully. However, on the third one, the exception is thrown.
If, I'm always using the same method for saving, why does it not always work? Is it an EF
or GraphDiff
issue and is there a way to solve this problem?
Most questions and related answers here on SO and elsewhere all deal with the situation where a child entity is being deleted. This is not the case in my particular scenario.
Probably is related to UserId not nullable. As you know, UserId is used by EF to write User. If User is null (it can) EF cannot write the entity. Also it could be related to User.GroupId that is used by EF to map the Group.Users collection.
Also, I can't understand your model. A User have one and only one Group. A Group can have more then one User.
If this is true, why don't you just simplify your model removing the association entity? EF does not need it...
public class Group
{
public int Id { get; set; }
public virtual ICollection<User> Users{ get; set; }
}
public class User
{
public int Id { get; set; }
public virtual Group Group { get; set; }
}
In this case you need just UserMap.
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
// You don't need to set primary key. EF take Id and create an identity column
//HasKey(t => t.Id);
// Table & Column Mappings
ToTable("users");
// No column mappings in this case.
// Relationships
HasRequired(t => t.Group)
.WithMany(t => t.Users)
.Map(d => d.MapKey("user_group"));
}
}
If a User can have more than one Group you can just modify your user model in this way
public class User
{
public int Id { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
EF understands that is a many to many relationship, will create the association table (UsersGroups) but you don't need the association entity.
EDIT
Rarely you need to set db.Entry(myEntity).State = EntityState.Modified;
with EF6. Proxies works quite good.