EF - 从父实体中删除关系

时间:2015-09-29 23:44:18

标签: c# asp.net-mvc entity-framework entity-framework-6

我们说我的模型中有两个类: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,它将M-N关系分成两个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()方法时,我收到以下错误:

  

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

有人能让我朝正确的方向努力解决这个错误吗?

2 个答案:

答案 0 :(得分:1)

再次编辑

我遇到的其他事情是你可能在关系的另一方面孤儿 - 你正在从产品中删除类别实体,但是你是否从该类别中删除了产品?查看WillCascadeOnDelete(Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship)或尝试在查询中包含关系 - 而不是_ctx.Products.Where(...),请使用_ctx.Products.Include(p => p.Categories).Where(...)

让我们知道这对您有何帮助!

<强>被修改

所以,基于洛伦佐的回答,我理解他正在做的事情。这是过去的一些事情(可能还有一些人)。

你可能正在考虑级联删除问题 - 选择3(现在这不是一个选项......)。

查看The relationship could not be changed because one or more of the foreign-key properties is non-nullable

主要问题是这个(据我所知,因为我们不知道CategoriesPerProduct的模式:我们的犯罪者):你将来自CategoriesPerProduct的条目置于一个不可能的状态,它需要一个Category存在,但是类别已经有效地设置为空。

因此,您有几个选择:

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

答案 1 :(得分:0)

我相信您正在获得该异常,因为您正在插入重复的项目。重复的项目将转换为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 );
    }
}

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

希望它有所帮助!