我想通过导航属性添加一些相关数据。
我有这些课程:
[EntityTypeConfiguration(typeof(EmotionConfiguration))]
public class Emotion
{
[Required]
public Guid Id { get; set; }
public virtual ICollection<LocalizedEmotionInfo> LocalizedInfos { get; set; } = null!;
}
internal sealed class EmotionConfiguration : IEntityTypeConfiguration<Emotion>
{
public void Configure(EntityTypeBuilder<Emotion> builder)
{
builder.HasKey(e => e.Id);
builder.HasMany(e => e.LocalizedInfos)
.WithOne(e => e.Emotion)
.HasForeignKey(e => e.EmotionId);
builder.Navigation(e => e.LocalizedInfos);
}
}
[EntityTypeConfiguration(typeof(LocalizedEmotionInfoConfiguration))]
public class LocalizedEmotionInfo
{
[Required]
public Guid Id { get; set; }
public string Lcid { get; set; } = null!;
public Guid EmotionId { get; set; }
public virtual Emotion Emotion { get; set; } = null!;
}
internal sealed class LocalizedEmotionInfoConfiguration : IEntityTypeConfiguration<LocalizedEmotionInfo>
{
public void Configure(EntityTypeBuilder<LocalizedEmotionInfo> builder)
{
builder.HasKey(e => e.Id);
builder.HasIndex(e => new { e.EmotionId, e.Lcid })
.IsUnique();
}
}
但是当我尝试通过导航属性添加
LocalizedEmotionInfo
时,就像
var localizedEmotionInfo = new LocalizedEmotionInfo()
{
Id = Guid.NewGuid(),
Lcid = "en-EN",
LocalizedName = "Anger",
LocalizedDesciption = "Some emotion",
};
var emotion = await _context.Emotions
.Where(e => e.Id == emotionId)
.Include(e => e.LocalizedInfos)
.SingleOrDefaultAsync();
if (emotion is null)
{
this._logger.LogWarning("Tried updating non existing emotion {Guid}", emotionId);
return null;
}
emotion.LocalizedInfos.Add(localizedEmotionInfo);
await _context.SaveChangesAsync();
我收到此错误
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was
expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or
deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962
for information on understanding and handling optimistic concurrency exceptions.
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
我使用最新版本的
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
并使用
IdentityDbContext
作为基类。
是的,我知道我可以直接将新对象添加到上下文中,但我想了解为什么这不起作用。因为我从 msdn 文档中了解到它应该开箱即用。
更新:
这就是新对象添加到导航属性后的
ChangeTracker.DebugView.LongView
(之前有2个,应该添加第三个)
Emotion {Id: 831d0e70-57be-4616-bf14-3b5a3f4b20d0} Unchanged
Id: '831d0e70-57be-4616-bf14-3b5a3f4b20d0' PK
Color: ''
IsCoreEmotion: 'True'
IsDeleted: 'False'
Name: 'Anger'
DefaultComposeParts: []
LocalizedInfos: [{Id: 1153a315-079f-4a11-a0ce-4e84beb8e622}, {Id: 9b8badff-b9d8-49bc-aa65-9b4d4101e7bb}, <not found>]
UserDefinedCompositions: []
LocalizedEmotionInfo {Id: 1153a315-079f-4a11-a0ce-4e84beb8e622} Unchanged
Id: '1153a315-079f-4a11-a0ce-4e84beb8e622' PK
EmotionId: '831d0e70-57be-4616-bf14-3b5a3f4b20d0' FK
Lcid: 'fr-FR'
LocalizedDesciption: 'string'
LocalizedName: 'string'
Emotion: {Id: 831d0e70-57be-4616-bf14-3b5a3f4b20d0}
LocalizedEmotionInfo {Id: 9b8badff-b9d8-49bc-aa65-9b4d4101e7bb} Unchanged
Id: '9b8badff-b9d8-49bc-aa65-9b4d4101e7bb' PK
EmotionId: '831d0e70-57be-4616-bf14-3b5a3f4b20d0' FK
Lcid: 'de-DE'
LocalizedDesciption: 'Wut is blöd'
LocalizedName: 'Wut'
Emotion: {Id: 831d0e70-57be-4616-bf14-3b5a3f4b20d0}
这是实体框架在
SaveChangesAsync
调用期间发送到数据库的 sql 语句。
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (2ms) [Parameters=[@p4='?' (DbType = Guid), @p0='?' (DbType = Guid), @p1='?', @p2='?', @p3='?'], CommandType='Text', CommandTimeout='30']
UPDATE "LocalizedEmotionInfos" SET "EmotionId" = @p0, "Lcid" = @p1, "LocalizedDesciption" = @p2, "LocalizedName" = @p3
WHERE "Id" = @p4;
所以看起来实体框架并没有将新实体注册为新实体,而只是想更改一个不存在的实体。
我发现问题了。
问题在于
var localizedEmotionInfo = new LocalizedEmotionInfo()
{
Id = Guid.NewGuid(),
Lcid = "en-EN",
LocalizedName = "Anger",
LocalizedDesciption = "Some emotion",
};
Id
(主键)被设置为非默认值。这无缝地让实体框架认为该实体对应于数据库中的现有行。
如果
Id
属性设置为 default
或根本没有设置,那么它就可以工作。