DbUpdateException при удалении объекта самоссылающегося класса - PullRequest
2 голосов
/ 24 мая 2019

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

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

Моя таблица изображений разделяется на два класса


public class Image(){
  public int Id{ get; set;}
  public int? MainImageId{ get; set;}; 
  public string Description{ get; set;}
  public virtual Image MainImage{ get; set;}
  public virtual ImageContent Content{ get; set;}
  public virtual ICollection<Image> Variations{ get; set;}
}
public class ImageContent(){
  public int ImageId { get; set; }
  public byte[] Raw { get; set; }
  public virtual Image Image { get; set; }
}

Они построены как

 modelBuilder.Entity<Image>()
  .HasRequired(x => x.Content)
  .WithRequiredDependent(x => x.Image);

modelBuilder.Entity<Image>()
  .ToTable("Images");            

modelBuilder.Entity<ImageContent>()
  .HasKey(x => x.ImageId)
  .ToTable("Images");

modelBuilder.Entity<Image>()
  .HasMany(i => i.Variations)
  .WithOptional(i => i.MainImage)
  .HasForeignKey(v => v.MainImageId);

И моя функция удаления -

public void Delete(int id){
  var image = context.Set<Image>().Include("Variations").FirstOrDefault(i => i.Id == id);

  if (image != null) {

    foreach(Image variation in image.Variations) {
      context.Set<Image>().Remove(variation);
    }

    context.Set<Image>().Remove(image);

    context.SaveChanges();
  }

}

Но когда я запускаю функцию Удалить, я получаю DbUpdateException с сообщением Invalid data encountered. A required relationship is missing. Examine StateEntries to determine the source of the constraint violation.

Ошибка возникает при запуске context.SaveChanges().

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

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

1 Ответ

1 голос
/ 25 мая 2019
 modelBuilder.Entity<Image>()
  .ToTable("Images");            

 modelBuilder.Entity<ImageContent>()
  .HasKey(x => x.ImageId)
  .ToTable("Images");

То, что вы здесь делаете, называется разбиение таблицы , то есть в базе данных есть одна таблица, Images, которая "разбивается" на две сущности,Image и ImageContent.Когда вы удаляете пару сущностей, которая содержит разделенную таблицу, один из способов сделать это - удалить сущность основная , пока включена зависимая сущность.Главная сущность определяется в отображении ...

 modelBuilder.Entity<Image>()
  .HasRequired(x => x.Content)
  .WithRequiredDependent(x => x.Image);

В которой говорится: Image является зависимым, поэтому ImageContent является основным.

Иесть проблема.

Строка ...

 context.Set<Image>().Remove(image);

... удаляет зависимую сущность.

Теперь вы можете легко это исправитьвключив Content, а также пометив его для удаления (пока игнорируя Variations):

using System.Data.Entity;
...

var image = context.Set<Image>().Include(i => i.Content)
                   .Single(i => i.Id == id);
context.Entry(image).State = EntityState.Deleted;
context.Entry(image.Content).State = EntityState.Deleted;

Но я думаю, что модель имеет больше смысла, когда Image является главной сущностью:

modelBuilder.Entity<Image>()
  .HasRequired(x => x.Content)
  .WithRequiredPrincipal(x => x.Image);

Теперь изображение может быть удалено без явного удаления Content:

var image = context.Set<Image>().Include(i => i.Content)
                   .Single(i => i.Id == id);
context.Entry(image).State = EntityState.Deleted;
// or context.Set<Image>().Remove(image);

Это также упрощает удаление вариантов:

var image = context.Set<Image>()
                   .Include(i => i.Content)
                   .Include(i => i.Variations.Select(v => v.Content))
                   .Single(i => i.Id == id);

foreach (var variation in image.Variations)
{
    context.Set<Image>().Remove(variation);
}
context.Set<Image>().Remove(image);

В качестве бонуса,это также позволяет вам сделать удаление менее дорогостоящим, только извлекая Image s из базы данных без их (большого) содержимого и удаляя содержимое с помощью заглушки .Я покажу вам, как это сделать только для одного изображения:

var img1 = context.Set<Image>(). Single(i => i.Id == 1);
img1.Content = new ImageContent { ImageId = img1.Id }; // Stub entity
context.Entry(img1.Content).State = Entity.EntityState.Unchanged; // Attach to context
context.Set<Image>().Remove(img1);
context.SaveChanges();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...