Бъг при използване на IDeletableEntity - свойствата, който сочат към друга таблица се зануляват при Remove


0

Здравейте,

след като добавих интерфейсите IDeletableEntity и IAuditInfo, които се имплементират от няколко класа, добавих и ApplyDeletableEntityRules() и ApplyAuditInfoRules() в AppDbContext класа, получавам много страннен дефект.

1) при модифициране на обект, няма проблем, в базата се променя ModifiedOn и всичко е ОК.

Дефекта се получава, когато се опитам да изтрия даден обект - стойността IsDeleted и DeletedOn се променят, но част от свойствата в базата се зануляват.

Полетата, които се зануляват сочат към други таблици и имам съответно виртуални свойства за всяко от тях, част от решението на проблема е да се махнат виртуалните свойства и вече не се зануляват, което мен не ме огрява. :>

Същото се получава и при ForumSystem, която писа Ники, давам пример как може да се счупи - добавяме още една таблица Author (int Id, string Name), Post сочи към Author (int AuthorId и virtual Author Author) и при изтриване на Post - AuthorId-то се занулява.

Някой, ако се е натъкнал на този проблем и има решение, нека да сподели.




Отговори



2

Както са писали по-горе смятам, че проблема е от това, че когато се промени ЕntityState на дадено entity на Deleted тогава свойствата, които са foreign keys се зануляват. За да разрешим този проблем може да изпълним стъпките по-долу:   

1. Премахваме метода: ApplyDeletableEntityRules() от ApplicationDbContext класа.

2. В класа GenericRepository<T> добавяме метода ChangeEntityState, като го използваме и в методите: Add, Update, Delete, Detach:

public void Add(T entity)
{
    this.ChangeEntityState(entity, EntityState.Added);
}

//...

public void ChangeEntityState(T entity, EntityState state)
{
    var entry = this.context.Entry(entity);
    if (entry.State == EntityState.Detached)
    {
        this.dbSet.Attach(entity);
    }

    entry.State = state;
}

3. В DeletableEntityRepository<T> създаваме 2 метода първият, от които (Delete) е методът за soft delete.

public override void Delete(T entity)
{
    entity.IsDeleted = true;
    entity.DeletedOn = DateTime.Now;
    base.ChangeEntityState(entity, EntityState.Modified);
}

public void HardDelete(T entity)
{
    base.Delete(entity);
}

*методът HardDelete го добавяме в случай, че в някой action метод наистина искаме да изтрием записи от базата.

4. В IDeletableEntityRepository<T> добавяме: void HardDelete(T entity);

Разрешението по-горе е тествано и работи коректно. Надявам се, че това ще свърши работа :)


от encounter (1041 точки)


1
Override на метода Delete() е работещ workaround :) Ще го покажем на следващото видео в уъркшопа.

от Nikolay.IT (39117 точки)


1
Колега и аз имам същия проблем. Идва от репото. Когато смениш стейта на Изтрит занулява (сега ще започна да търся защо). Ако сложиш за тестване "entry.State = EntityState.Modified;" ще видиш, че няма да занули.
EDIT: Не съм пробвал още, но като хак може да override-неш Delete метода в Delete репото.
EDIT 2: Закоментирах методите в DBContext...да не минава през тях. В дебъг минава без Exception, но не се случва нищо...рънтайм гърми! :(
EDIT 3: По-долу писах как го оправих!

от d.kostov88 (1086 точки)


0
Аз тествах директно през AppDbContext, не съм минавал през Repository.

от martin.nikolov (4535 точки)

0
Да, проблема е другаде. Виж писах по-долу.

от d.kostov88 (1086 точки)


0
И аз имах същия проблем преди няколко дни.
Това, което видях при дебъга, като гледах и в кода на EntityFramework, е че EntityFramework нулира навигационните properties (външните ключове) на ентитито, когато му се смени статуса на Deleted. Затова дори и да се промени на Modified в ApplyDeletableEntityRules метода след това, те вече са нулирани.
Засега и аз обмислям вариант за override на Delete метода от GenericRepository в DeletableEntityRepository или за добавяне на един нов метод SoftDelete в него. Но трябва също да се модифицира и ApplyDeletableEntityRules в ApplicationDbContext.
Ако някой стигне до по-хитро решение нека сподели :)

от pavlinadrosos (950 точки)


0
Да и проблема идва от [Required](и най-вероятно всяко друго пропърти).
Оправих го така:
Вместо да слагам [Required] на навигационното пропърти добавих още едно от примитивен тип.
[Required] public int CategoryId { get; set; } public virtual Category Category { get; set; }
[Required] public string OwnerId { get; set; }
public virtual User Owner { get; set; }

от d.kostov88 (1086 точки)

0
Това пак се чупи, в случая, ако няма виртуално property, полетата няма да се зануляват, но проблема е, че ние в общия случай имаме едно свойство от примитивен тип за Id-то и едно виртуално навигационно за удобство.
Ако е [Required] направо ще гръмне с грешка, защото ще иска да го изтрие, ако не е [Required] тихично ще го занули. :>

от martin.nikolov (4535 точки)