c# - Entity Framework Core 使用 CurrentValues.SetValues 从其他实体更新实体属性,由于实体上的主键而失败
问题描述
请在此处查看我的原始 SO 问题 - Entity Framework Core - 基于通过 Web API 传入的实体的 JSON 表示来更新具有子实体的有效方法
这个问题详细说明了我正在使用 .NET Core、Entity Framework Core 和 PostgreSQL 构建 API。我试图通过 API 引入一个对象,将其转换为一种Customer
实体,然后使用它来更新现有Customer
实体,因此基本上采用在原始和更新之间发生变化的所有修改属性,并生成更新语句。
由于以下错误,此操作失败:
System.InvalidOperationException:实体类型“Customer”上的属性“CustomerInternalId”是键的一部分,因此无法修改或标记为已修改。要使用标识外键更改现有实体的主体,首先删除依赖项并调用“SaveChanges”,然后将依赖项与新主体关联。
但是,我不清楚如何在要求 .NET Core / EF Core 只保留CustomerInternalId
现有记录的主键时执行更新?
显然我不想更改主键,这可能是我不想更新的一件事。传入数据可能会更改所有其他属性。
这是我的示例方法(它已完成测试的一半,因此在我试图弄清楚这一点时,那里有一些日志记录等)。
/// <summary>
/// Update customer record - still working on this
/// </summary>
/// <param name="customerPayload"></param>
public static void UpdateCustomerRecord(CustomerPayload customerPayload)
{
try
{
var updateCustomer = customerPayload.Convert(customerPayload);
Console.WriteLine($"Update customer created from payload");
using (var loyalty = new loyaltyContext())
{
Console.WriteLine($"Using context to get db customer by mca id {updateCustomer.McaId}");
var customer = loyalty.Customer
.Include(c => c.ContactInformation)
.Include(c => c.Address)
.Include(c => c.MarketingPreferences)
.Include(c => c.ContentTypePreferences)
.Include(c => c.ExternalCards)
.Where(c => c.McaId == updateCustomer.McaId).First();
var cu = (from c in loyalty.Customer
where c.McaId == updateCustomer.McaId
select c).ToList();
Console.WriteLine($"Customer guid from linq query {cu.First().CustomerInternalId}");
Console.WriteLine(
$"db customer Id {customer.CustomerInternalId} incoming customer Id {updateCustomer.CustomerInternalId}");
loyalty.Entry(customer).CurrentValues.SetValues(updateCustomer);
loyalty.Entry(customer).State = EntityState.Modified;
Console.WriteLine($"customer last name: {customer.LastName}");
loyalty.SaveChanges();
//TODO expand code to cover scenarios such as an additional address on an udpate
}
}
catch (ArgumentNullException e)
{
Console.WriteLine(e);
throw new CustomerNotFoundException();
}
catch (Exception ex)
{
Console.WriteLine($"{ex}");
}
}
}
解决方案
您可以使用以下通用替换方法,该方法将跳过阴影(类似于原始)和关键属性:
public static void UpdateProperties(this DbContext context, object target, object source) { foreach (var propertyEntry in context.Entry(target).Properties) { var 属性 = propertyEntry.Metadata; // 跳过阴影和关键属性 if (property.IsShadowProperty() || (propertyEntry.EntityEntry.IsKeySet && property.IsKey())) 继续; propertyEntry.CurrentValue = property.Getter().GetClrValue(source); } }
(对于 EF Core 2.x 替换IsShadowProperty()
为IsShadowProperty
)
现在您可以简单地使用它:
忠诚度.UpdateProperties(客户,更新客户);
旁注:以下行
忠诚度.Entry(客户).State = EntityState.Modified;
应该被删除。已customer
被跟踪,因此 EF 将智能地仅更新已修改的属性(或根本不更新),而这会强制更新所有属性,无论是否更改。
推荐阅读
- sql - 如何从 microsoft sql 数据库中删除重复数据(仅在结果上)
- vnc - 如何设置游戏手柄以使用 Apache guacamole?
- woocommerce - 在js中添加超链接属性以链接
- javascript - 标签之间的 Safari 14.1 localStorage 未同步
- tensorflow - 如何使用条件来破坏张量流函数?
- azure - Azure ML 实时终结点中的“{not_found} 不在索引中”
- google-colaboratory - 有没有办法使用 Colab 从路径中获取 Google Drive 文件夹的 ID?
- ruby-on-rails - 为什么 RuboCop 用“做”这个词代替“应该”这个词?
- java - 在已部署的 Spring Boot 应用程序中配置 Logback Appender
- amazon-web-services - 在另一个构造中使用在一个 cdk 构造中创建的资源