EF Core | Добавление связанной сущности | Ожидалось, что операция базы данных повлияет на 1 строку(и), но на самом деле C#

Место общения программистов C#
Ответить
Anonymous
 EF Core | Добавление связанной сущности | Ожидалось, что операция базы данных повлияет на 1 строку(и), но на самом деле

Сообщение Anonymous »

Не могу понять, почему появляется следующая ошибка:
{
"title": "Server Error",
"status": 500,
"detail": "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."
}

Это полное исключение:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException
HResult=0x80131500
Message=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.
Source=Npgsql.EntityFrameworkCore.PostgreSQL
StackTrace:
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.d__10.MoveNext()
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.d__7.MoveNext()
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.d__50.MoveNext()
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.d__50.MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.d__9.MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.d__9.MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.d__9.MoveNext()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__111.MoveNext()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__115.MoveNext()
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.d__7`2.MoveNext()
at Microsoft.EntityFrameworkCore.DbContext.d__63.MoveNext()
at Persistence.IKATDbContext.d__43.MoveNext() in C:\Users\\source\repos\ikat\Persistence\IKATDbContext.cs:line 54

This exception was originally thrown at this call stack:
[External Code]
Persistence.IKATDbContext.SaveEntitiesAsync(System.Threading.CancellationToken) in IKATDbContext.cs

Я единственный, кто тестирует в локальной среде. Я работал в соответствии с документацией здесь
Код вроде бы тоже на месте, но я наверняка что-то упускаю. Обратите внимание, что я упростил код, удалив проверку и т. д.
У меня есть две сущности:
public class Tutor : Entity, IAggregateRoot
{
public Guid Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public ICollection Availabilities { get; private set; }

public Tutor(string firstName, string lastName)
{
this.Id = Guid.NewGuid();
this.FirstName = firstName;
this.LastName = lastName;
this.Availabilities = [];
}

public static Tutor Create(string firstName, string lastName)
{
return new Tutor(firstName, lastName);
}

public Availability AddAvailability(DateTime startDateTime, DateTime endDateTime, bool recurring)
{
Availability availability = new(this.Id, startDateTime, endDateTime, recurring);
this.Availabilities.Add(availability);
return availability;
}
}

И:
public class Availability : Entity
{
public Guid Id { get; private set; }
public Guid TutorId { get; private set; }
public Tutor Tutor { get; private set; }
public DateTime StartDateTime { get; private set; }
public DateTime EndDateTime { get; private set; }
public bool Recurring { get; private set; }

public Availability(Guid tutorId, DateTime startDateTime, DateTime endDateTime, bool recurring)
{
this.Id = Guid.NewGuid();
this.TutorId = tutorId;
this.StartDateTime = startDateTime;
this.EndDateTime = endDateTime;
this.Recurring = recurring;
}
}

Это конфигурация объекта:
public class TutorEntityConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("Tutors");

builder.HasKey(t => t.Id);

builder.Property(t => t.FirstName)
.IsRequired();

builder.Property(t => t.LastName)
.IsRequired();

builder.HasMany(t => t.Availabilities)
.WithOne(a => a.Tutor)
.HasForeignKey(a => a.TutorId);
}
}

И:
public class AvailabilityEntityConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("Availabilities");

builder.HasKey(a => a.Id);

builder.Property(a => a.StartDateTime)
.IsRequired();

builder.Property(a => a.EndDateTime)
.IsRequired();

builder.Property(a => a.Recurring)
.IsRequired();

builder.HasOne(a => a.Tutor)
.WithMany(t => t.Availabilities)
.HasForeignKey(a => a.TutorId)
.IsRequired();
}
}

Я использую методы TutorRepository:
public void Update(Tutor tutor)
{
this.context.Entry(tutor).State = EntityState.Modified;
}

public async Task GetTutorByIdAsync(Guid tutorId, CancellationToken cancellationToken)
{
return await this.context.Tutors
.Include(t => t.Availabilities)
.FirstOrDefaultAsync(t => t.Id == tutorId, cancellationToken);
}

Наконец, CommandHandler:
public async Task Handle(CreateAvailabilityCommand request, CancellationToken cancellationToken)
{
Tutor tutor = await this.tutorRepository.GetTutorByIdAsync(request.TutorId, cancellationToken) ?? throw new Exception("Tutor not found");
tutor.AddAvailability(request.StartTime, request.EndTime, false);

this.tutorRepository.Update(tutor);
_ = await this.tutorRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
return tutor.Id;
}

Обновление
public class CreateAvailabilityCommandHandler : IRequestHandler {
private readonly IKATDbContext context;

public CreateAvailabilityCommandHandler(IKATDbContext context)
{
this.context = context;
}

public async Task Handle(CreateAvailabilityCommand request, CancellationToken cancellationToken)
{
Tutor tutor = await this.context.Tutors
.Include(t => t.Availabilities)
.SingleOrDefaultAsync(t => t.Id == request.TutorId, cancellationToken) ?? throw new Exception("Tutor not found");

_ = tutor.AddAvailability(request.StartTime, request.EndTime, false);

Console.WriteLine(context.ChangeTracker.DebugView.LongView);

_ = await this.context.SaveChangesAsync(cancellationToken);
return tutor.Id;
} }

Это результат. Как вы можете видеть, новая доступность не найдена, поскольку она не отслеживается EF.
Tutor {Id: b8eab345-b775-49c8-b9d9-5aac35aa80e5} Unchanged
Id: 'b8eab345-b775-49c8-b9d9-5aac35aa80e5' PK
FirstName: 'goose'
LastName: 'pen'
Availabilities: []

При явном добавлении в контекст это работает. Поскольку EF отслеживает это.
Availability av = tutor.AddAvailability(request.StartTime, request.EndTime, false);

this.context.Add(av);

Это долгосрочная перспектива:
Availability {Id: 6d920ed3-c718-4429-8039-813aa750afe3} Added
Id: '6d920ed3-c718-4429-8039-813aa750afe3' PK
EndDateTime: '06/25/2024 01:52:48'
Recurring: 'False'
StartDateTime: '06/25/2024 01:52:47'
TutorId: 'b8eab345-b775-49c8-b9d9-5aac35aa80e5' FK
Tutor: {Id: b8eab345-b775-49c8-b9d9-5aac35aa80e5}
Tutor {Id: b8eab345-b775-49c8-b9d9-5aac35aa80e5} Unchanged
Id: 'b8eab345-b775-49c8-b9d9-5aac35aa80e5' PK
FirstName: 'goose'
LastName: 'pen'
Availabilities: [{Id: 6d920ed3-c718-4429-8039-813aa750afe3}]


Подробнее здесь: https://stackoverflow.com/questions/785 ... d-to-affec
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»