繁体   English   中英

EF-从上级实体删除关系

[英]EF - Remove a relationship from parent entity

假设我的模型中有两个类: ProductCategory

public class Product
{
    public Product() {
        this.Categories = new HashSet<Category>();
    }

    [...]

    public virtual ICollection<Category> Categories { get; set; }
}

public class Category
{
    public Category() {
        this.Products = new HashSet<Product>();
    }

    [...]

    public virtual ICollection<Product> Products { get; set; }
}

产品具有许多类别,并且类别适用于许多产品。 为了对此关系建模,我在OnModelCreating方法中具有以下代码:

    modelBuilder.Entity<Product>()
        .HasMany( p => p.Categories )
        .WithMany( p => p.Products )
        .Map( m => {
            m.MapLeftKey( "ProductID" );
            m.MapRightKey( "CategoryID" );
            m.ToTable( "CategoriesPerProduct" );
        } );
    modelBuilder.Entity<Category>()
        .HasMany( p => p.Products )
        .WithMany( p => p.Categories )
        .Map( m => {
            m.MapLeftKey( "CategoryID" );
            m.MapRightKey( "ProductID" );
            m.ToTable( "CategoriesPerProduct" );
        } );

这将创建一个新表CategoriesPerProduct ,该表将MN关系分为两个1-N关系,这很适合我的需求。

我现在需要更新与产品相关的类别,并且为了简化我的代码,我确实决定删除所有现有类别,然后像以下示例中一样重新添加新类别:

ICollection<Category> productCategories = product.Categories;
//First remove all existing categories 
foreach ( var category in productCategories ) {
    product.Categories.Remove( category );
}
// ..then add the new ones from input
foreach ( string categoryName in categories ) {
    Category c = await _ctx.Categories.SingleOrDefaultAsync( p => p.Description == categoryName );
    if ( c != null ) {
        product.Categories.Add( pc );
    }
    else {
        c = new ProductCategory() { Description = categoryName };
        _ctx.Categories.Add( c );
        await _ctx.SaveChangesAsync();
        product.Categories.Add( c );
    }
}
await _ctx.SaveChangesAsync();

不幸的是,当代码击中事务Commit()方法时,出现以下错误:

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

有人可以驱使我朝正确的方向解决这个错误吗?

再次编辑

我发生的另一件事是,您可能正在孤立关系的另一端-您要从产品中删除类别实体,但是您要从该类别中删除产品吗? 查看WillCascadeOnDelete( 一对一或一对一关系的实体框架(EF)代码第一个级联删除 )或尝试在查询中包括这些关系-而不是_ctx.Products.Where(...) ,请使用_ctx.Products.Include(p => p.Categories).Where(...)

让我们知道如何为您服务!

编辑

因此,根据洛伦佐的回应,我了解他正在尝试做的事情。 过去,这是困扰我(也许还有几个人)的事情。

您可能正在查看“级联删除”问题-选择选项3(现在已不是必需的选项...)。


看一下该关系无法更改,因为一个或多个外键属性不可为空

主要问题是这个(据我所知,因为我们不知道CategoriesPerProduct的模式:我们的作案者):您正在将CategoriesPerProduct中的条目置于不可能的状态,该状态要求存在Category,但是Category已有效地设置为null。

因此,您有几种选择:

  1. 使外键可为空(不推荐)
  2. 直接从DbContext中删除该项目(类似_ctx.Categories.Remove(category);
  3. 将状态标记为已删除(类似ctx.Entry(category).State = EntityState.Deleted

我相信您会收到该异常,因为您要插入重复的项目。 重复的项目将转换为ProductCategoryID = 0 ,因此您将获得异常。

在您的当前代码中进行以下更改:

ICollection<Category> productCategories = product.Categories;

//Make sure that you are iterating a copy of categories, using ToList()
//If you do not use ToList() you might get an exception saying that you changed the list you are iterating

foreach ( var category in productCategories.ToList()) {
    product.Categories.Remove( category );
}

// ..then add the new ones from input
// ... but remember to add only the new ones
var currentCategories = productCategories.Select(i => i.Name);
// you could use another strategy here

foreach ( string categoryName in categories.Except(currentCategories)) {
    Category c = await _ctx.Categories.SingleOrDefaultAsync( p => p.Description == categoryName );
    if ( c != null ) {
        product.Categories.Add( pc );
    } else {
        c = new ProductCategory() { Description = categoryName };
        //_ctx.Categories.Add( c ); NOT NECESSARY - IT WILL BE AUTOMATICALLY INSERTED 
        //await _ctx.SaveChangesAsync(); NOT NECESSARY
        product.Categories.Add( c );
    }
}

未经测试的代码,但应该可以正常工作。

希望能帮助到你!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM