无法跟踪实体,因为备用键属性为空



我一直在努力让这个工作,但没有成功。

架构:WPF应用程序在Azure WebApp上添加、更新、接收和删除实体。. NET Core ReST API和JWT)数据库仅在WebApp中使用实体框架核心制作。

当我第一次添加一个"事件"实体时,它确实工作得很好。甚至是"第一轮"的更新。无缝工作。但是如果我关闭WPF应用程序并尝试更新,它不起作用并抛出异常,无论我在代码中修改什么都不起作用。

无法跟踪类型为"事件"的实体,因为备用键属性"UniqueId"为空。如果在关系中不使用备用键,则考虑使用唯一索引。唯一索引可以包含空值,而备用键不能包含空值。

UniqueId不是ID,它将作为可能出现也可能不出现的报表的外键使用。但可以肯定的是,UniqueId永远不会为空。我不知道为什么它总是这样告诉我。

任何想法?

<事件/strong>

internal class IncidentConfiguration : IEntityTypeConfiguration<Incident>
{
internal static IncidentConfiguration Create() => new();
public void Configure(EntityTypeBuilder<Incident> builder)
{
builder
.Property(incident => incident.Id)
.ValueGeneratedOnAdd();
builder
.Property(incident => incident.RowVersion)
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
builder
.Property(incident => incident.UniqueId)
.HasField("_uniqueId")
.IsRequired();
builder
.Property(incident => incident.Completion)
.HasField("_completion");
builder
.Property(incident => incident.Status)
.HasField("_status");
builder
.Property(incident => incident.Estimated)
.HasField("_estimated")
.HasConversion(new TimeSpanToTicksConverter());
builder
.Property(incident => incident.Actual)
.HasField("_actual")
.HasConversion(new TimeSpanToTicksConverter());
builder
.Property(incident => incident.Closed)
.HasField("_closed");
builder
.Property(incident => incident.Comments)
.HasField("_comments");
builder
.Property(incident => incident.Opened)
.HasField("_opened");
builder
.Property(incident => incident.Updated)
.HasField("_updated");
builder
.Property(incident => incident.BriefDescripion)
.HasField("_briefDescripion");
builder
.Property(incident => incident.Project)
.HasField("_project");
builder
.Ignore(incident => incident.IsUpdated);
}
}

internal class ReportConfiguration : IEntityTypeConfiguration<Report>
{
internal static ReportConfiguration Create() => new();
public void Configure(EntityTypeBuilder<Report> builder)
{
builder
.Property(report => report.Id)
.ValueGeneratedOnAdd();
builder
.Property(report => report.RowVersion)
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
builder
.HasOne(report => report.Incident)
.WithMany(incident => incident.Reports)
.HasForeignKey(report => report.UniqueId)
.HasPrincipalKey(incident => incident.UniqueId)
.OnDelete(DeleteBehavior.Cascade);
builder
.Ignore(report => report.IsUpdated);
}
}

"Update"方法

public async Task<bool> UpdateAsync(Common.Models.Incident incident)
{
_manualResetEvent.WaitOne();
try
{
using var context = new IncidentManagerContext(_connectionString);                
context.Incidents.Update(incident);
bool saveFailed;
do
{
saveFailed = false;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
catch (Exception) { return false; }
finally { _manualResetEvent.Set(); }
return true;
}

最后我想到了以下解决方案:"context.Incidents.Update(事件);"仍然行不通,而且可能永远都行不通。我把它改成

context.Entry(await context.Incidents.FirstOrDefaultAsync(x => x.Id == incident.Id)).CurrentValues.SetValues(incident);

不再有幽灵条目,也没有null错误,但导航属性现在已经消失或无法识别。

如何解决这个问题?从自动模式切换到更多的手动模式。我为事件数据模型实现了可空的外键属性。

private int? _supporterId, _customerId;
/// <summary>
/// The supporter's foreign key
/// </summary>
[JsonPropertyName("supporterId")]
public int? SupporterId { get { return _supporterId; } set { SetProperty(ref _supporterId, value); } }
/// <summary>
/// The customer's foreign key
/// </summary>
[JsonPropertyName("customerId")]
public int? CustomerId { get { return _customerId; } set { SetProperty(ref _customerId, value); } }

实体类型配置的变化(事件)

builder
.Property(incident => incident.SupporterId)
.HasField("_supporterId");
builder
.Property(incident => incident.CustomerId)
.HasField("_customerId");

实体类型配置的变化(Customer)

builder
.HasMany(customer => customer.Incidents)
.WithOne(incident => incident.Customer)
.HasForeignKey(incident => incident.CustomerId)
.OnDelete(DeleteBehavior.NoAction);

实体类型配置的变化(支持者)

builder
.HasMany(supporter => supporter.Incidents)
.WithOne(incident => incident.Supporter)
.HasForeignKey(incident => incident.SupporterId)
.OnDelete(DeleteBehavior.NoAction);

最后,通过这些手动实现和分配的可空外键,导航属性在从数据库读取时具有预期的值。

但我仍然有一种不好的感觉,这只是一个变通办法,有些事情不像它应该是。如果你有更好的主意,欢迎在这里分享。

最新更新