EF Core 8 中通过导航属性添加相关数据

问题描述 投票:0回答:1

我想通过导航属性添加一些相关数据。

我有这些课程:

[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;

所以看起来实体框架并没有将新实体注册为新实体,而只是想更改一个不存在的实体。

c# postgresql entity-framework entity-framework-8
1个回答
0
投票

我发现问题了。

问题在于

var localizedEmotionInfo = new LocalizedEmotionInfo()
{
    Id = Guid.NewGuid(),
    Lcid = "en-EN",
    LocalizedName = "Anger",
    LocalizedDesciption = "Some emotion",
};

Id
(主键)被设置为非默认值。这无缝地让实体框架认为该实体对应于数据库中的现有行。

如果

Id
属性设置为
default
或根本没有设置,那么它就可以工作。

© www.soinside.com 2019 - 2024. All rights reserved.