实体框架硬级联删除 [英] Entity Framework hard cascade delete

查看:176
本文介绍了实体框架硬级联删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个与实体框架映射的SQLite数据库。
有2个表:集合(1:n)专辑。



当我删除集合时,所有相关的相册也必须删除。
我使用 CollectionRepo.Delete(collection); 来实现。它使用以下代码:

  public int Delete(Collection entity)
{
Context.Entry实体).State = EntityState.Deleted;
return Context.SaveChanges();
}

问题是:当我执行此代码时, Context.SaveChanges(); 给我一个例外:


操作失败:关系无法更改因为一个或多个外键属性不可为空。当对关系进行更改时,将相关的外键属性设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,否则必须删除不相关的对象。


似乎Entity Framework想要外键上的 null 而不是删除条目。但是这绝对不是我想要的,因为没有父母的专辑没有任何意义(至少在我的用例中)。



我可以明显地手动删除专辑,然后删除空集合,但在我看来有点棘手。首先,在我看来,EF应该足够聪明才能自己做,以简化代码;其次,如果我有几十个关系到集合和专辑,我会结束相当大的,难以维护的,代码库






集合类

  public class Collection 
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id {get;组; }

public virtual List< Album>专辑{get;组; } = new List< Album>();
}

专辑类

  public class Album 
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id {get;组;

[必需]
[ForeignKey(Collection)]
public long CollectionId {get;组; }

public virtual Collection Collection {get;组;
}

DbContext子类

  public class DataEntities:DbContext 
{
public virtual DbSet< Collection>收藏{get;组; }
public virtual DbSet< Album>专辑{get;组;

public DataEntities():base(name = Connection)
{
Configuration.ProxyCreationEnabled = false;
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity< Album>()
.HasRequired(a => a .Collection)
.WithMany(c => c.Albums)
.HasForeignKey(a => a.CollectionId)
.WillCascadeOnDelete(true);
modelBuilder.Entity< Collection>()
.HasMany(c => c.Albums)
.WithRequired(a => a.Collection)
.WillCascadeOnDelete(true );
}
}


解决方案

分离对象图修改在EF中一直不清楚。这是其中一个失败的情况之一,没有很好的理由。



假设集合实体传递给删除方法有相册集合填充(至少这是我能够重现异常)。行

  Context.Entry(entity).State = EntityState.Deleted; 

做了两件事:附加实体和所有相册对象从 entity.Albums 到上下文,标记实体 as 删除,和(注意!)相册对象为修改。当您调用 SaveChanges 时,会导致错误的行为,最终会生成有问题的异常。



有解决这个错误行为的两种方法(解决方法)。



第一个是用

  Context.Collections.Attach(实体); 
Context.Collections.Remove(entity);

效果与上述类似,导入和区别现在相关的相册标记为的文件被删除,允许成功执行 SaveChanges / p>

缺点是现在, SaveChanges 发出一个 DELETE 在$ code> Collection 的命令之前,每个相册的命令,这是低效的,并没有太多意义,因为级联删除将在数据库内完美处理。



第二个选项是保留代码,但在附加实体之前清除相关的集合:

  entity.Albums = null; 
Context.Entry(entity).State = EntityState.Deleted;

这允许成功执行 SaveChanges 并生成单个 DELETE 命令仅适用于该实体。



缺点是您需要编写其他代码,而不要忘记支持级联删除的任何子集合,而不是为需要级联更新的集合(即可选)关系需要用 NULL 更新FK字段。



选择是你的。


I have a SQLite DB mapped with Entity Framework. There are 2 tables : Collections (1:n) Albums.

When I delete a collection, all related albums have to be deleted as well. I use CollectionRepo.Delete(collection); to achieve that. It uses the following code :

public int Delete(Collection entity)
{
    Context.Entry(entity).State = EntityState.Deleted;
    return Context.SaveChanges();
}

The problem is: when I execute this code, Context.SaveChanges(); give me an exception:

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

It seems that Entity Framework wants to null on the foreign keys instead of deleting the entries. But this is absolutely not what I want because an album makes no sense without a parent (in my use case at least).

I could obviously manualy delete the albums first and then delete the empty collection but it seems to me a bit tricky. First, it seems to me that EF should be smart enough to do it on it's own to simplify the code and second, what if I have dozens of relations to collections and albums, I would end up with quite a big, hard to maintain, code base.


Collection Class

public class Collection
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }

    public virtual List<Album> Albums { get; set; } = new List<Album>();
}

Album class

public class Album
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }

    [Required]
    [ForeignKey("Collection")]
    public long CollectionId { get; set; }

    public virtual Collection Collection { get; set; }
}

DbContext child class

public class DataEntities : DbContext
{
    public virtual DbSet<Collection> Collections { get; set; }
    public virtual DbSet<Album> Albums { get; set; }

    public DataEntities() : base("name=Connection")
    {
        Configuration.ProxyCreationEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Album>()
            .HasRequired(a => a.Collection)
            .WithMany(c => c.Albums)
            .HasForeignKey(a => a.CollectionId)
            .WillCascadeOnDelete(true);
        modelBuilder.Entity<Collection>()
            .HasMany(c => c.Albums)
            .WithRequired(a => a.Collection)
            .WillCascadeOnDelete(true);
    }
}

解决方案

Applying detached object graph modifications has always been unclear in EF. This is one of the cases where it fails without a good reason.

Assuming the Collection entity passed to the Delete method has Albums collection populated (at least this is how I was able to reproduce the exception). The line

Context.Entry(entity).State = EntityState.Deleted;

does two things: attaches entity and all Album objects from the entity.Albums to the context, marks entity as Deleted, and (note!) the Album objects as Modified. This leads to incorrect behavior when you call SaveChanges, and at the end generates the exception in question.

There are two ways (workarounds) to fix this incorrect behavior.

The first one is to replace the above line with

Context.Collections.Attach(entity);
Context.Collections.Remove(entity);

The effect is similar to the described above, with the importand difference that now the related Album objects arte marked as Deleted, which allows successfully executing the SaveChanges.

The drawback is that now the SaveChanges issues a DELETE command for each Album before the command for deleting the Collection, which is inefficient and doesn't make much sense since the cascade delete would handle that perfectly inside the database.

The second option is to keep the code as is, but clear the related collection before attaching the entity:

entity.Albums = null;
Context.Entry(entity).State = EntityState.Deleted;

This allows successfully executing SaveChanges and it generates a single DELETE command only for the entity.

The drawback is that you need to write additional code and not forget any child collection which supports cascade delete, and not doing that for collections that need cascade update (i.e. with optional relation which requires updating the FK field with NULL).

The choice is yours.

这篇关于实体框架硬级联删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆