错误:"Update"调用 Cosmos DB 和实体框架核心 3.1.2 时"alternate key property 'id' is null"



假设我有一个PersonModel的简单模型,如下所示,我从ASP.NET Core 3.1 Blazor或MVC UI(web表单(更新FirstName属性:

public class PersonModel 
{
public Guid PersonModelId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

我将模型发送到http触发的Azure函数并绑定到该模型。这就是我实现EF Core并执行以下更新的地方:

public async Task<int> UpdateAsync(TEntity entity)
{
_dbSet.Attach(entity); //<--this throws the error
_dbContext.Entry(entity).State = EntityState.Modified;
return await _dbContext.SaveChangesAsync();
}

我用这个模型创建或删除实体没有问题,在下面的文章中,我看不到创建一个从任何特定的存储库需求(如分区密钥或主键等("干净"的模型有任何问题,但我似乎无法运行更新代码而不出现以下错误:

无法跟踪"PersonModel"类型的实体,因为备用密钥属性"id"为null。

是否有人知道模型的具体要求,并使用此方法更新实体?有没有一个替代方案像docs文章中的例子一样简单/优雅?

我尝试过_dbContext.Update(entity),但我得到了完全相同的错误,这让我相信我的模型需要一个"ID"属性。

我还尝试修改我的DbContext设置,如下所示:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonModel>().HasAlternateKey(p => p.PersonModelId);
}

但这只会给我同样的错误,也不会改变实体的存储方式。

更新

根据以下公认答案的指导,我写了以下更改以使其发挥作用:

public async Task<int> UpdateAsync(TEntity entity)
{
var entry = _dbSet.Add(entity);
entry.State = EntityState.Unchanged;
_dbContext.Update(entity);
return await _dbContext.SaveChangesAsync();
}

编辑:我认为这个问题是由EF Core为每个名为"id"的项目自动生成的属性引起的,特别是所有这些项目都是小写的,并且与模型中定义的键分离。似乎是Cosmos数据库提供商特有的,空值问题在EF Core->数据库提供商->Cosmos的文档中"使用断开连接的实体"一节中注明。

显然,EF Core只有在实体进入"已添加"状态时才会生成此属性。因此,当您调用Attach时,此"id"属性为null,因此出现异常。文档中列出了一个变通方法,并给出了比我所能给出的更好的解释。

以下链接到Cosmos提供商的EF Core文档页面。滚动到概览页面的底部,找到涵盖此内容的断开实体部分。

https://learn.microsoft.com/en-us/ef/core/providers/cosmos/?tabs=dotnet-core-cli#处理断开连接的实体

根据这个问题[1],您需要获取条目并显式设置项目id:

public async Task<T> UpdateItemAsync(string id, T item)
{
var itemEntry = context.Entry(item);            
itemEntry.Property<string>("id").CurrentValue = "Item|" + id;
itemEntry.State = EntityState.Modified;            
await context.SaveChangesAsync();
return itemEntry.Entity;
}

[1]https://github.com/dotnet/efcore/issues/15289

最新更新