enz*_*nzi 5 c# entity-framework foreign-key-relationship sql-update entity-framework-5
抱歉这个模糊的标题,很难用一行描述:
我有2个实体User和UserAddress,其中用户有2个外键DefaultInvoiceAddressId和DefaultDeliveryAddressId和UserAddress有一个UserId外键.
用户对象具有默认地址(DefaultInvoiceAddress和DefaultDeliveryAddress)的导航属性以及用于其所有地址的导航属性:AllAddresses.
映射等工作,创建和更新用户和地址也有效.
但是不起作用的是将用户的现有地址设置为例如DefaultInvoiceAddress.在SQL术语中,我想要发生的是UPDATE USER SET DefaultInvoiceAddressId = 5 WHERE Id = 3.
我试过以下方式:
private void MarkAs(User user, UserAddress address, User.AddressType type) {
if (context.Entry(user).State == EntityState.Detached)
context.Users.Attach(user);
// guess I don't really need this:
if (context.Entry(address).State == EntityState.Detached)
context.UserAddresses.Attach(address);
if (type.HasFlag(User.AddressType.DefaultInvoice)) {
user.DefaultInvoiceAddressId = address.Id;
user.DefaultInvoiceAddress = null;
context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
}
if (type.HasFlag(User.AddressType.DefaultDelivery)) {
user.DefaultDeliveryAddressId = address.Id;
user.DefaultDeliveryAddress = null;
context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
}
}
Run Code Online (Sandbox Code Playgroud)
在创建新的UserAddresses以及更新地址时都会调用此方法.创建方案按预期工作,但在更新的情况下,我收到以下错误:
The changes to the database were committed successfully,
but an error occurred while updating the object context.
The ObjectContext might be in an inconsistent state.
Inner exception message: A referential integrity constraint violation occurred:
The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
Run Code Online (Sandbox Code Playgroud)
我用一个User对象调用该方法,我从数据库中检索它和它包含的DefaultDeliveryAddress,我通过eager loading加载它.
var user = mainDb.User.Get(UnitTestData.Users.Martin.Id, User.Include.DefaultAddresses);
var existingAddress = user.DefaultDeliveryAddress;
mainDb.User.Addresses.SetAs(user, existingAddress, User.AddressType.DefaultInvoice))
// the SetAs method verfies input parameters, calls MarkAs and then SaveChanges
Run Code Online (Sandbox Code Playgroud)
简而言之,我只想让用户的DefaultDeliveryAddress也是他的DefaultInvoiceAddress,这可以通过上面的SQL Update命令轻松完成,但我遗漏了我的EF代码.我已经检查过了:
DefaultInvoiceAddress)被重置为nullModified(使用调试器检查),因为其中一个属性被标记为已修改我怀疑这个问题是由于User实体有2个UserAddress引用,并且两个外键都设置为引用相同的地址 - 我怎样才能让EF使用它?
更新:
以下是User实体的映射:
// from UserMap.cs:
...
Property(t => t.DefaultInvoiceAddressId).HasColumnName("DefaultInvoiceAddressId");
Property(t => t.DefaultDeliveryAddressId).HasColumnName("DefaultDeliveryAddressId");
// Relationships
HasOptional(t => t.DefaultInvoiceAddress)
.WithMany()
.HasForeignKey(t => t.DefaultInvoiceAddressId);
HasOptional(t => t.DefaultDeliveryAddress)
.WithMany()
.HasForeignKey(t => t.DefaultDeliveryAddressId);
HasMany(t => t.AllAddresses)
.WithRequired()
.HasForeignKey(t => t.UserId)
.WillCascadeOnDelete();
Run Code Online (Sandbox Code Playgroud)
UserAddress没有返回用户的导航属性; 它只有contanis HasMaxLength和HasColumnName设置(我排除它们以保持问题有点可读).
更新2
这是Intellitrace执行的命令:
The command text "update [TestSchema].[User]
set [DefaultInvoiceAddressId] = @0
where ([Id] = @1)
" was executed on connection "Server=(localdb)\..."
Run Code Online (Sandbox Code Playgroud)
对我来说很好看; 似乎只有EF状态管理器才会被键映射混淆.
找出问题:显然,将导航属性设置为null会产生很大的不同,因为EF可能会将其解释为预期的更改/更新(至少这是我怀疑的).
以下版本的MarkAs方法有效:
private void MarkAs(User user, UserAddress address, User.AddressType type) {
if (context.Entry(user).State == EntityState.Detached) {
// clear navigation properties before attaching the entity
user.DefaultInvoiceAddress = null;
user.DefaultDeliveryAddress = null;
context.Users.Attach(user);
}
// address doesn't have to be attached
if (type.HasFlag(User.AddressType.DefaultInvoice)) {
// previously I tried to clear the navigation property here
user.DefaultInvoiceAddressId = address.Id;
context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
}
if (type.HasFlag(User.AddressType.DefaultDelivery)) {
user.DefaultDeliveryAddressId = address.Id;
context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
}
}
Run Code Online (Sandbox Code Playgroud)
总结我对未来读者的发现:
我不会马上接受我自己的回答,让其他(更有资格的)读者有机会回答; 如果在接下来的两天内没有发布答案,我会接受这个答案.