EF 6代码首先,使用包含导航属性更改外键Id会导致“发生了引用完整性约束违反”;错误
本文关键字:发生了 Id 引用 完整性约束 错误 代码 属性 导航 包含 EF | 更新日期: 2023-09-27 18:17:17
我正在使用实体框架6.1.3,并有这样的场景,我检索一个实体与其导航属性(使用Include()),并从上下文断开连接,更改外键Id,然后将其重新连接到新的DbContext:
// Init the Db
using (var db = new MyContext())
{
var theWarranty = new ProductWarranty { WarrantyName = "The Warranty" };
var newWarranty = new ProductWarranty { WarrantyName = "New Warranty" };
var brand = new ProductBrand { BrandName = "The Brand", DefaultWarranty = theWarranty };
db.ProductBrands.Add(brand);
db.ProductWarranties.Add(newWarranty);
db.SaveChanges();
}
// Load the detached Brand
ProductBrand detachedBrand;
using (var db = new MyContext())
{
detachedBrand = db.ProductBrands.AsNoTracking()
.Include(b => b.DefaultWarranty) // <<< If this line is removed the Attach works
.First(x => x.Id == 1);
}
// Modify the Default Warranty Foreign Key
detachedBrand.DefaultWarranty = null;
detachedBrand.DefaultWarranty_Id = 2;
// Attempt to re-attach and save the changes
using (var db = new MyContext())
{
var entity = db.Set<ProductBrand>().Attach(detachedBrand); // <<< This line throws the exception
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}
我:
发生了一个引用完整性约束违反:>' productwarty '的属性值。关系一端的Id'与'ProductBrand '的属性值不匹配。"DefaultWarranty_Id"
但是,如果我不使用Include(),则附加工作正常。
在现实世界的场景中,我确实需要导航属性(defaultwguaranty),但我看不出在分离实体中包含导航与在分离实体中不加载导航有什么区别。根据我的经验和阅读,应该将外键设置为新值,并将navigation属性设置为null。
我已经阅读了Ladislav的博客上的外键与独立属性http://www.ladislavmrnka.com/2011/05/foreign-key-vs-independent-associations-in-ef-4/,但它并没有完全处理这种情况,从我可以告诉我在这种情况下使用外键。
发生了什么,什么是正确的方法来处理改变外键与包含的导航属性,像这种情况?
这几乎就像EF在使用Include时没有"完全"分离实体…这看起来也很奇怪。
下面是简化的设置:
<标题> 产品品牌public partial class ProductBrand
{
public int Id { get; set; }
public string BrandName { get; set; }
public Nullable<int> DefaultWarranty_Id { get; set; }
public virtual ProductWarranty DefaultWarranty { get; set; }
}
产品品牌图
public class ProductBrandMap : EntityTypeConfiguration<ProductBrand>
{
public ProductBrandMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.BrandName)
.IsRequired()
.HasMaxLength(40);
// Table & Column Mappings
this.ToTable("ProductBrands");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.BrandName).HasColumnName("BrandName");
this.Property(t => t.DefaultWarranty_Id).HasColumnName("DefaultWarranty_Id");
// Relationships
this.HasOptional(t => t.DefaultWarranty)
.WithMany(t => t.ProductBrands)
.HasForeignKey(d => d.DefaultWarranty_Id)
.WillCascadeOnDelete(false);
}
}
<标题>产品保证h1> 品保修图public class ProductWarrantyMap : EntityTypeConfiguration<ProductWarranty>
{
public ProductWarrantyMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.WarrantyName)
.IsRequired()
.HasMaxLength(40);
// Table & Column Mappings
this.ToTable("ProductWarranties");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.WarrantyName).HasColumnName("WarrantyName");
}
}
标题>标题>
当启用ProxyCreationEnabled
时,即使实体没有被任何上下文跟踪,它们也以某种方式在内部相互连接。我的意思是,动态代理实体仔细记录FK中的任何更改,以强制执行引用完整性。因此,只需关闭ProxyCreationEnabled
:
public MyContext()
{
this.Configuration.ProxyCreationEnabled = false;
}
但如果你问我的意见,我更喜欢在实体被跟踪时更改它们,修改后将它们分离。
我在尝试分配一个新的foreign_key值时遇到了同样的问题。
我在更新核心对象上的外键后执行附加,这是一个问题。
相反,我现在附加到对象上,然后将更改应用于字段。
public static bool ChangeForeignKeyAssocation(baseobject existing, int newFK, bool throwOnError = true) {
try {
using (var e = new Entities()) {
e.table.Attach(existing);
e.Entry(existing).State = EntityState.Modified;
existing.related_table_id = newFK;
int result = sbe.SaveChanges();
return (result == 1);
}
} catch (Exception ex) {
//LogIt.E(ex);
if (throwOnError) {
throw ex;
} else {
return false;
}
}
}