Jam*_*son 8 c# entity-framework entity-framework-core .net-core
请在此处查看我原来的 SO 问题 - Entity Framework Core - effective way to update Entity that haschildren based on JSONrepresentation ofEntity being pass in via Web API
这个问题详细说明了我正在使用 .NET Core、Entity Framework Core 和 PostgreSQL 构建 API。我试图通过 API 引入一个对象,将其转换为一种Customer实体类型,然后使用它来更新现有Customer实体,因此基本上采用原始和更新之间已更改的所有修改属性,并生成更新语句。
由于以下错误,此操作失败:
System.InvalidOperationException:实体类型“Customer”上的属性“CustomerInternalId”是键的一部分,因此无法修改或标记为已修改。要更改具有标识外键的现有实体的主体,首先删除依赖项并调用“SaveChanges”,然后将依赖项与新主体关联。
CustomerInternalId但是我不清楚,如何在要求 .NET Core / EF Core 保留现有记录的主键的同时执行更新?
显然我不想更改主键,这可能是我不想更新的一件事。所有其他属性都可能被传入数据更改。
这是我的示例方法(测试已经进行了一半,所以当我试图弄清楚这一点时,其中有一些日志记录等)。
/// <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}");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这比我想象的要简单。我所需要做的就是更新“传入”客户,使其具有与现有客户记录相同的内部 ID,然后就无需更新,没有问题!代码非常简单,在这里发布似乎毫无意义,但以防万一:
/// <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);
using (var loyalty = new loyaltyContext())
{
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();
updateCustomer.CustomerInternalId = customer.CustomerInternalId;
loyalty.Entry(customer).CurrentValues.SetValues(updateCustomer);
loyalty.SaveChanges();
}
}
catch (ArgumentNullException e)
{
Console.WriteLine(e);
throw new CustomerNotFoundException();
}
catch (Exception ex)
{
Console.WriteLine($"{ex}");
}
}
Run Code Online (Sandbox Code Playgroud)
重点是:
updateCustomer.CustomerInternalId = customer.CustomerInternalId;
Run Code Online (Sandbox Code Playgroud)
您可以使用以下通用替换方法,该方法将跳过阴影(类似于原始)和关键属性:
public static void UpdateProperties(此 DbContext 上下文,对象目标,对象源)
{
foreach (context.Entry(target).Properties 中的 var propertyEntry)
{
var 属性 = propertyEntry.Metadata;
// 跳过阴影和关键属性
if (property.IsShadowProperty() || (propertyEntry.EntityEntry.IsKeySet && property.IsKey())) 继续;
propertyEntry.CurrentValue = property.GetGetter().GetClrValue(source);
}
}
(对于 EF Core 2.x 替换IsShadowProperty()为IsShadowProperty)
现在你可以简单地使用它:
忠诚度.UpdateProperties(客户, updateCustomer);
旁注:以下行
忠诚度.Entry(客户).State = EntityState.Modified;
应该被删除。已customer被跟踪,因此 EF 将仅智能地更新修改的属性(或根本不更新),而这会强制更新所有属性,无论是否更改。