我们说我的模型中有两个类:Product
和Category
。
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进行更改时 关系,相关的外键属性设置为空值。 如果外键不支持空值,则为新关系 必须定义外键属性必须另外分配 必须删除非空值或不相关的对象。
有人能让我朝正确的方向努力解决这个错误吗?
答案 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(现在这不是一个选项......)。
主要问题是这个(据我所知,因为我们不知道CategoriesPerProduct的模式:我们的犯罪者):你将来自CategoriesPerProduct的条目置于一个不可能的状态,它需要一个Category存在,但是类别已经有效地设置为空。
因此,您有几个选择:
_ctx.Categories.Remove(category);
)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 );
}
}
未经测试的代码,但它应该可以正常工作。
希望它有所帮助!