Невозможно изменить отношение, так как одно или несколько свойств внешнего ключа не могут быть равны нулю - PullRequest
172 голосов
/ 04 апреля 2011

Я получаю эту ошибку, когда я получаю GetById () для сущности, а затем задаю для коллекции дочерних сущностей мой новый список, который поступает из представления MVC.не может быть изменено, потому что одно или несколько свойств внешнего ключа не могут иметь значение NULL.Когда в отношение вносится изменение, для соответствующего свойства внешнего ключа устанавливается нулевое значение.Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, свойству внешнего ключа должно быть назначено другое ненулевое значение или несвязанный объект должен быть удален.

Iне совсем понимаю эту строку:

Отношение не может быть изменено, поскольку одно или несколько свойств внешнего ключа не могут быть обнуляемыми.изменить отношения между двумя организациями?Он должен оставаться неизменным на протяжении всего жизненного цикла всего приложения.

Код, в котором возникает исключение, заключается в простом присвоении измененных дочерних классов в коллекции существующему родительскому классу.Надеемся, что это поможет удалить дочерние классы, добавить новые и модификации.Я бы подумал, что Entity Framework справится с этим.

Строки кода можно перевести в:

var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();

Ответы [ 18 ]

1 голос
/ 07 февраля 2017

Я использовал решение Моша , но мне было неочевидно, как правильно реализовать ключ композиции в коде.

Итак, вот решение:

public class Holiday
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int HolidayId { get; set; }
    [Key, Column(Order = 1), ForeignKey("Location")]
    public LocationEnum LocationId { get; set; }

    public virtual Location Location { get; set; }

    public DateTime Date { get; set; }
    public string Name { get; set; }
}
1 голос
/ 03 декабря 2013

Я пробовал эти и многие другие решения, но ни одно из них не сработало.Так как это первый ответ в Google, я добавлю свое решение здесь.

Метод, который мне пригодился, заключался в том, чтобы вынимать отношения из картинки во время коммитов, поэтому EF ничего не испортил,Я сделал это, заново найдя родительский объект в DBContext и удалив его.Поскольку все свойства навигации вновь найденного объекта равны нулю, дочерние отношения игнорируются при фиксации.

var toDelete = db.Parents.Find(parentObject.ID);
db.Parents.Remove(toDelete);
db.SaveChanges();

Обратите внимание, что это предполагает, что внешние ключи настроены с помощью ON DELETE CASCADE, поэтому, когда родительская строкаудаляется, дети будут очищены базой данных.

1 голос
/ 25 февраля 2014

Этот тип решения помог мне:

Parent original = db.Parent.SingleOrDefault<Parent>(t => t.ID == updated.ID);
db.Childs.RemoveRange(original.Childs);
updated.Childs.ToList().ForEach(c => original.Childs.Add(c));
db.Entry<Parent>(original).CurrentValues.SetValues(updated);

Важно сказать, что это удаляет все записи и вставляет их снова. Но для моего случая (меньше 10) это нормально.

Надеюсь, это поможет.

0 голосов
/ 23 апреля 2019

Когда я собирался удалить свою запись, у меня возникла та же проблема, чем возникла какая-то проблема, потому что решение этой проблемы состоит в том, что когда вы собираетесь удалить свою запись, чем вы что-то упустили перед удалением заголовка / основной записи, вы должны написать чтобы код для удаления его детали перед заголовком / Master Я надеюсь, что ваш вопрос будет решен.

0 голосов
/ 28 января 2019

Используя решение Slauma, я создал несколько общих функций, чтобы помочь обновить дочерние объекты и коллекции дочерних объектов.

Все мои постоянные объекты реализуют этот интерфейс

/// <summary>
/// Base interface for all persisted entries
/// </summary>
public interface IBase
{
    /// <summary>
    /// The Id
    /// </summary>
    int Id { get; set; }
}

С этим Iреализовал эти две функции в моем репозитории

    /// <summary>
    /// Check if orgEntry is set update it's values, otherwise add it
    /// </summary>
    /// <param name="set">The collection</param>
    /// <param name="entry">The entry</param>
    /// <param name="orgEntry">The original entry found in the database (can be <code>null</code> is this is a new entry)</param>
    /// <returns>The added or updated entry</returns>
    public T AddOrUpdateEntry<T>(DbSet<T> set, T entry, T orgEntry) where T : class, IBase
    {
        if (entry.Id == 0 || orgEntry == null)
        {
            entry.Id = 0;
            return set.Add(entry);
        }
        else
        {
            Context.Entry(orgEntry).CurrentValues.SetValues(entry);
            return orgEntry;
        }
    }

    /// <summary>
    /// check if each entry of the new list was in the orginal list, if found, update it, if not found add it
    /// all entries found in the orignal list that are not in the new list are removed
    /// </summary>
    /// <typeparam name="T">The type of entry</typeparam>
    /// <param name="set">The database set</param>
    /// <param name="newList">The new list</param>
    /// <param name="orgList">The original list</param>
    public void AddOrUpdateCollection<T>(DbSet<T> set, ICollection<T> newList, ICollection<T> orgList) where T : class, IBase
    {
        // attach or update all entries in the new list
        foreach (T entry in newList)
        {
            // Find out if we had the entry already in the list
            var orgEntry = orgList.SingleOrDefault(e => e.Id != 0 && e.Id == entry.Id);

            AddOrUpdateEntry(set, entry, orgEntry);
        }

        // Remove all entries from the original list that are no longer in the new list
        foreach (T orgEntry in orgList.Where(e => e.Id != 0).ToList())
        {
            if (!newList.Any(e => e.Id == orgEntry.Id))
            {
                set.Remove(orgEntry);
            }
        }
    }

Чтобы использовать его, я делаю следующее:

var originalParent = _dbContext.ParentItems
    .Where(p => p.Id == parent.Id)
    .Include(p => p.ChildItems)
    .Include(p => p.ChildItems2)
    .SingleOrDefault();

// Add the parent (including collections) to the context or update it's values (except the collections)
originalParent = AddOrUpdateEntry(_dbContext.ParentItems, parent, originalParent);

// Update each collection
AddOrUpdateCollection(_dbContext.ChildItems, parent.ChildItems, orgiginalParent.ChildItems);
AddOrUpdateCollection(_dbContext.ChildItems2, parent.ChildItems2, orgiginalParent.ChildItems2);

Надеюсь, это поможет


ДОПОЛНИТЕЛЬНО: Вы могли бы такжесоздайте отдельный класс DbContextExtentions (или ваш собственный контекстный интерфейс):

public static void DbContextExtentions {
    /// <summary>
    /// Check if orgEntry is set update it's values, otherwise add it
    /// </summary>
    /// <param name="_dbContext">The context object</param>
    /// <param name="set">The collection</param>
    /// <param name="entry">The entry</param>
    /// <param name="orgEntry">The original entry found in the database (can be <code>null</code> is this is a new entry)</param>
    /// <returns>The added or updated entry</returns>
    public static T AddOrUpdateEntry<T>(this DbContext _dbContext, DbSet<T> set, T entry, T orgEntry) where T : class, IBase
    {
        if (entry.IsNew || orgEntry == null) // New or not found in context
        {
            entry.Id = 0;
            return set.Add(entry);
        }
        else
        {
            _dbContext.Entry(orgEntry).CurrentValues.SetValues(entry);
            return orgEntry;
        }
    }

    /// <summary>
    /// check if each entry of the new list was in the orginal list, if found, update it, if not found add it
    /// all entries found in the orignal list that are not in the new list are removed
    /// </summary>
    /// <typeparam name="T">The type of entry</typeparam>
    /// <param name="_dbContext">The context object</param>
    /// <param name="set">The database set</param>
    /// <param name="newList">The new list</param>
    /// <param name="orgList">The original list</param>
    public static void AddOrUpdateCollection<T>(this DbContext _dbContext, DbSet<T> set, ICollection<T> newList, ICollection<T> orgList) where T : class, IBase
    {
        // attach or update all entries in the new list
        foreach (T entry in newList)
        {
            // Find out if we had the entry already in the list
            var orgEntry = orgList.SingleOrDefault(e => e.Id != 0 && e.Id == entry.Id);

            AddOrUpdateEntry(_dbContext, set, entry, orgEntry);
        }

        // Remove all entries from the original list that are no longer in the new list
        foreach (T orgEntry in orgList.Where(e => e.Id != 0).ToList())
        {
            if (!newList.Any(e => e.Id == orgEntry.Id))
            {
                set.Remove(orgEntry);
            }
        }
    }
}

и используйте его следующим образом:

var originalParent = _dbContext.ParentItems
    .Where(p => p.Id == parent.Id)
    .Include(p => p.ChildItems)
    .Include(p => p.ChildItems2)
    .SingleOrDefault();

// Add the parent (including collections) to the context or update it's values (except the collections)
originalParent = _dbContext.AddOrUpdateEntry(_dbContext.ParentItems, parent, originalParent);

// Update each collection
_dbContext.AddOrUpdateCollection(_dbContext.ChildItems, parent.ChildItems, orgiginalParent.ChildItems);
_dbContext.AddOrUpdateCollection(_dbContext.ChildItems2, parent.ChildItems2, orgiginalParent.ChildItems2);
0 голосов
/ 07 июля 2017

Я также решил свою проблему с помощью ответа Моша , и я подумал, что Ответ PeterB был немного, поскольку он использовал перечисление в качестве внешнего ключа.Помните, что вам нужно будет добавить новую миграцию после добавления этого кода.

Я также могу порекомендовать этот блог для других решений:

http://www.kianryan.co.uk/2013/03/orphaned-child/

Код:

public class Child
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public string Heading { get; set; }
    //Add other properties here.

    [Key, Column(Order = 1)]
    public int ParentId { get; set; }

    public virtual Parent Parent { get; set; }
}
0 голосов
/ 30 января 2017

Эта проблема возникает из-за того, что мы пытаемся удалить родительскую таблицу, но данные дочерней таблицы присутствуют.Мы решаем проблему с помощью каскадного удаления.

В модели Create метод в классе dbcontext.

 modelBuilder.Entity<Job>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                .WithRequired(C => C.Job)
                .HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
            modelBuilder.Entity<Sport>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                  .WithRequired(C => C.Sport)
                  .HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);

После этого, в нашем вызове API

var JobList = Context.Job                       
          .Include(x => x.JobSportsMappings)                                     .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();

Каскадное удаление опция удаляет родительскую, а также родительскую дочернюю таблицу с помощью этого простого кода.Сделайте это простым способом.

Remove Range, который использовался для удаления списка записей в базе данных. Спасибо

0 голосов
/ 05 января 2015

Я столкнулся с этой проблемой раньше, чем через несколько часов, и попробовал все, но в моем случае решение отличалось от перечисленного выше.

Если вы используете уже извлеченный объект из базы данных и пытаетесь изменить его дочерний объектошибка произойдет, но если вы получите свежую копию сущности из базы данных, проблем быть не должно.Не используйте это:

 public void CheckUsersCount(CompanyProduct companyProduct) 
 {
     companyProduct.Name = "Test";
 }

Используйте это:

 public void CheckUsersCount(Guid companyProductId)
 {
      CompanyProduct companyProduct = CompanyProductManager.Get(companyProductId);
      companyProduct.Name = "Test";
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...