Ошибка удаления дочерней сущности; удаленный дочерний объект помечается как измененный, а не как удаленный - PullRequest
0 голосов
/ 17 марта 2019

У меня возникла непредвиденная проблема при попытке удалить дочернюю сущность из одной родительской записи базы данных.После выполнения некоторых тестов мы повторили проблему.Наш код C # использует базу данных Northwind.

NWModel context = new NWModel();
Orders order = context.Orders.Where(w => w.OrderID == 10248).FirstOrDefault();
context.Entry(order).Collection(typeof(Order_Details).Name).Load();
order.Order_Details.RemoveAt(1);
System.Data.Entity.Infrastructure.DbChangeTracker changeset = context.ChangeTracker;
var changes = changeset.Entries().Where(x => x.State == System.Data.Entity.EntityState.Modified).ToList<Object>();
var detetions = changeset.Entries().Where(x => x.State == System.Data.Entity.EntityState.Deleted).ToList<Object>();

Все отлично работает с оригинальной настройкой таблицы Order_Details.

Удачи var правильно получили удаленную запись.

Для воспроизведениявыпуск, мы добавили в таблицу Order_Details новое поле PK identity в int (OrderDetailId int identity);после этого: var удалений не содержат записи, в то время как var changes содержат Order_Detail запись.

EF set Orders свойствоиз Order_Detail , чтобы обнулить и пометить запись как Обновлено .

Я нашел много статей по этой проблеме, все они предлагают пометить Order_Detail Заказы свойство до [Обязательно] .

Я пытался установить атрибут [Обязательный] на объекте FK, как предложено в этой записи , (в этой статье описывается поведение EFCore, такое же, как поведение EF6) но это не решает мою проблему.

Ожидается ли такое поведение?

Мы будем благодарны за любые комментарии и предложения.

Спасибо

Ответы [ 2 ]

1 голос
/ 18 марта 2019

Следуя предложению tschmit007, наконец-то мы реализовали этот обходной путь.

На Заказы сущность, которую мы использовали ObservableListSource <> вместо ICollection <> ; в ObservableListSource <> класс, void RemoveItem метод переопределения, мы могли бы управлять ctx.Set<orderDetail>().Remove(detail) методом. Таким образом, удаление всех дочерних записей работает как положено.

Здесь реализован код:

public partial class Orders
{
public Orders()
{
    Order_Details = new ObservableListSource<Order_Details>();
}
[Key]
public int OrderID { get; set; }
……………
public virtual ObservableListSource<Order_Details> Order_Details { get; set; }
}


public class ObservableListSource<T> : ObservableCollection<T>, IListSource
        where T : class
{
    private IBindingList _bindingList;

    bool IListSource.ContainsListCollection { get { return false; } }

    IList IListSource.GetList()
    {
        return _bindingList ?? (_bindingList = this.ToBindingList());
    }
    private bool _bRemoveInProgress = false;
    protected override void RemoveItem(int index)
    {
        if (!_bRemoveInProgress && index>=0)
        {
            _bRemoveInProgress = true;
            DbContext cntx = this[index].GetDbContextFromEntity();
            Type tp = this[index].GetDynamicProxiesType();
            cntx.Set(tp).Remove(this[index]);
            base.RemoveItem(index);
        }
        _bRemoveInProgress = false;
    }
}


public static class DbContextExtender
{
    public static Type GetDynamicProxiesType(this object entity)
    {
        var thisType = entity.GetType();
        if (thisType.Namespace == "System.Data.Entity.DynamicProxies")
            return thisType.BaseType;
        return thisType;
    }
    public static DbContext GetDbContextFromEntity(this object entity)
    {
        var object_context = GetObjectContextFromEntity(entity);

        if (object_context == null)
            return null;

        return new DbContext(object_context, dbContextOwnsObjectContext: false);
        //return object_context;
    }

    private static ObjectContext GetObjectContextFromEntity(object entity)
    {
        var field = entity.GetType().GetField("_entityWrapper");

        if (field == null)
            return null;

        var wrapper = field.GetValue(entity);
        var property = wrapper.GetType().GetProperty("Context");
        var context = (ObjectContext)property.GetValue(wrapper, null);

        return context;
    }
}
1 голос
/ 18 марта 2019

AFAIK, это правильное поведение.

Действительно, вы не удаляете детали. Вы удаляете его из коллекции деталей заказа, то есть обрезаете связь между заказом и деталями. Поскольку это отношение материализуется с помощью свойства навигации на стороне детализации, происходят две вещи:

  • коллекция обновлена,
  • свойство навигации установлено на ноль.

Логически с этого момента, если вы SaveChanges, у вас должно быть исключение, потому что деталь не может существовать без заказа, а вы еще не удалили деталь, только обрежьте отношение. Так что вы должны ctx.Set<orderDetail>().Remove(detail). (немного раздражает)

Вот почему в этом случае я обычно использую композитный ключ для деталей: detailId + orderId.

Поэтому, когда вы удаляете деталь, для orderId устанавливается значение null <=>, PK считается равным null => объект помечается для удаления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...