Връзката не може да бъде променена, тъй като едно или повече от свойствата на външния ключ не могат да бъдат нулеви при запазване

Използвам GraphDiff за актуализиране на графики на отделен обект и получавам горното изключение, когато запазвам родител и неговия деца.

Моделите и картографирането са:

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);
    }
}

За вмъквания и актуализации имам следния метод в моето хранилище:

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))
        );
}

Правя следното:

  1. Добавете нова група, запазете и презаредете
  2. Добавете потребител към групата, запазете и презаредете
  3. Добавете втори потребител, запазете и презаредете
  4. Добавете трети потребител, опитайте да запазите -> изключение е хвърлено

Докато се отстраняват грешки, състоянието на добавените потребителски обекти винаги се отделя и GroupId и Id са зададени на 0 за нови обекти. Първите 2 добавени потребители са запазени успешно. Въпреки това, на третия, изключението е хвърлено.

Ако винаги използвам един и същ метод за запазване, защо не работи винаги? Дали това е EF или GraphDiff проблем и има ли начин да се реши този проблем?

Повечето въпроси и свързаните с тях отговори тук на SO и другаде се занимават със ситуацията, при която дъщерен обект се изтрива. Това не е така в моя конкретен сценарий.


person Ivan-Mark Debono    schedule 14.04.2016    source източник


Отговори (3)


Вероятно е свързано с UserId, който не може да се нулира. Както знаете, UserId се използва от EF за писане на User. Ако User е null (може), EF не може да напише обекта. Също така може да е свързано с User.GroupId, който се използва от EF за картографиране на колекцията Group.Users.

Освен това не мога да разбера вашия модел. Потребителят има една и само една група. Една група може да има повече от един потребител.

Ако това е вярно, защо просто не опростите модела си, като премахнете обекта на асоцииране? EF не се нуждае от това...

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; }
}

В този случай ви трябва само 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"));

    }
}

Ако един потребител може да има повече от една група, можете просто да промените вашия потребителски модел по този начин

public class User
{
    public int Id { get; set; }
    public virtual ICollection<Group> Groups { get; set; }
}

EF разбира, че това е връзка много към много, ще създаде таблицата за асоцииране (UsersGroups), но нямате нужда от обекта за асоцииране.

РЕДАКТИРАНЕ

Рядко трябва да зададете db.Entry(myEntity).State = EntityState.Modified; с EF6. Прокситата работят доста добре.

person bubi    schedule 29.04.2016

Опитахте ли да видите дали състоянието на обекта се променя, когато не се проследява от EF?

Опитвам

db.Entry(currentUser).State = EntityState.Modified;

Функцията ToList() също може да ви накара да промените случайно състоянието на потребителя.

person user3145912    schedule 24.04.2016
comment
Промяната на ToList() на нормален foreach не направи никаква разлика. - person Ivan-Mark Debono; 24.04.2016
comment
Ако „състоянието“ е добавено и не е непроменено, това звучи като проблем с прекъсване на връзката на обекти. Предполагам, че EF пресъздава „GroupUser“, след което нулира предишния „GroupUser“, когато се опитва да запази. Опитахте ли да отпечатате стойностите на предишните добавени потребители? - person user3145912; 24.04.2016

моля, опитайте това. все пак не е тествано

public override Group Update(Group entity)
{
    if (entity.Users != null)
    {
        foreach(var user in entity.Users){
          user.GroupId = x.Id == 0 ? 0 : entity.Id;
          user.UserId = x.User.Id;
         dbContext.Entry(child).State = EntityState.Modified;
        }

    }

    return dbContext.UpdateGraph<Group>
        (entity,
            map => map.OwnedCollection(x => x.Users, with => with.AssociatedEntity(c => c.Users))
        );
}
person suulisin    schedule 28.04.2016