[Route("api/[controller]")]
public class BusinessProposalController(IBusinessProposalRepository businessProposalRepository, IWorkTaskRepository workTaskRepository, IWorkTaskTypeRepository workTaskTypeRepository, IUnitOfWork unitOfWork): ControllerBase
{
[HttpPost]
public async Task Create([FromBody] CreateBusinessProposalRequest request, CancellationToken cancellationToken)
{
// add request body validator
var businessProposal = request.ToEntity();
businessProposalRepository.Create(businessProposal);
var workTasks = request.WorkTasks.Select(w => w.ToEntity(businessProposal.Id)).ToList();
foreach (var workTask in workTasks)
{
var workTaskType = await workTaskTypeRepository.GetById(workTask.WorkTaskTypeId, cancellationToken);
if (workTaskType == null)
{
return BadRequest(new { message = $"Work task type {workTask.WorkTaskTypeId} not found"});
}
workTaskRepository.Create(workTask);
}
await unitOfWork.Commit(cancellationToken);
return Ok(new { id = businessProposal.Id});
}
}
Запрос с этим телом:
{
"name": "test",
"customer": "test",
"address": "test",
"workTasks": [
{
"unitCost": 20.0,
"quantity": 3,
"workTaskTypeId": "d4a1f4fc-148a-4a91-ae8b-6c364fc9a558"
}
]
}
выдает эту ошибку, когда я удаляю строку, которая проверяет, находится ли worktaskType уже в базе данных:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 19: 'FOREIGN KEY constraint failed'.
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteDataReader.NextResult()
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
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 Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at ReportGenerator.Infra.Repositories.UnitOfWork.Commit(CancellationToken cancellationToken) in C:\Users\pedro.ferrari\Desktop\dev\work\report-generator\ReportGenerator\ReportGenerator.Infra\Repositories\UnitOfWork.cs:line 12
at ReportGenerator.API.Controllers.BusinessProposalController.Create(CreateBusinessProposalRequest request, CancellationToken cancellationToken) in C:\Users\pedro.ferrari\Desktop\dev\work\report-generator\ReportGenerator\ReportGenerator.API\Controllers\BusinessProposalController.cs:line 37
at lambda_method5(Closure, Object)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5294
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: pt-BR,pt
Content-Type: application/json
Origin: http://localhost:5294
Referer: http://localhost:5294/swagger/index.html
Content-Length: 205
sec-ch-ua: "Not)A;Brand";v="99", "Brave";v="127", "Chromium";v="127"
sec-ch-ua-platform: "Windows"
sec-ch-ua-mobile: ?0
Sec-GPC: 1
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Дело в том, что я предварительно загружаю в базу данных все WorkTaskTypes, полученные из другого источника. Я могу получить доступ к этим значениям с помощью другой конечной точки, которая получает все типы рабочих задач, как здесь:
[img]https: //i.sstatic.net/A29G6Et8.png[/img]
и у меня много регистров:

Отлаживая код, я обнаружил, что не могу найти WorkTaskType , указав идентификатор при создании моей сущности в методе ToEntity:
public static class WorkTaskMapper
{
public static WorkTask ToEntity(this CreateWorkTaskRequest request, Guid BusinessProposalId)
{
return new WorkTask
{
BusinessProposalId = BusinessProposalId,
UnitCost = request.UnitCost,
Quantity = request.Quantity,
WorkTaskTypeId = request.WorkTaskTypeId
};
}
}
Это не указывает ни на какой WorkTaskType и не вызывает ошибку ограничения FK.
Может ли кто-нибудь мне помочь? Я сейчас немного растерялся
РЕДАКТИРОВАТЬ 1
WorkTask объект:
public class WorkTask : BaseEntity
{
public decimal UnitCost { get; set; }
public decimal Cost => UnitCost * Quantity;
public int Quantity { get; set; }
public Guid BusinessProposalId { get; set; }
public BusinessProposal BusinessProposal { get; set; } = null!;
public Guid WorkTaskTypeId { get; set; }
public WorkTaskType WorkTaskType { get; set; } = null!;
}
WorkTaskType объект:
namespace ReportGenerator.Domain.Entities;
public class WorkTaskType: BaseEntity
{
public string Description { get; set; } = null!;
public decimal UnitCost { get; set; }
public string UnitType { get; set; } = null!;
public string Category { get; set; } = null!;
public int LaborId { get; set; } = 0;
public ICollection WorkTasks { get; set; } = [];
}
EDIT 2
Базовый класс репозитория:
public class BaseRepository(AppDbContext context) : IBaseRepository where T : BaseEntity
{
protected readonly AppDbContext Context = context;
public void Create(T entity)
{
entity.DateCreated = DateTimeOffset.UtcNow;
Context.Add(entity);
}
public void Update(T entity)
{
entity.DateUpdated = DateTimeOffset.UtcNow;
Context.Update(entity);
}
public void Delete(T entity)
{
entity.DateDeleted = DateTimeOffset.UtcNow;
Context.Remove(entity);
}
public IQueryable GetByIdAsQueryable(Guid id)
{
return Context.Set().Where(x => x.Id == id).AsQueryable();
}
public IQueryable GetAllAsQueryable()
{
return Context.Set().ToList().AsQueryable();
}
public async Task GetById(Guid id, CancellationToken cancellationToken)
{
return await Context.Set().FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
}
public async Task GetAll(CancellationToken cancellationToken)
{
return await Context.Set().ToListAsync(cancellationToken);
}
}
Другие классы — это просто реализация Base:
namespace ReportGenerator.Infra.Repositories;
public class WorkTaskRepository(AppDbContext context): BaseRepository(context), IWorkTaskRepository
{
}
У меня есть конфигурация, которая загружается в класс AppDbContext:
public class WorkTaskTypeConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.HasKey(e => e.Id);
builder
.HasMany(e => e.WorkTasks)
.WithOne(e => e.WorkTaskType)
.HasForeignKey(e => e.WorkTaskTypeId);
}
}
Это создает ModelSnapshot, например:
//
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using ReportGenerator.Infra.Context;
#nullable disable
namespace ReportGenerator.Infra.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
modelBuilder.Entity("ReportGenerator.Domain.Entities.BusinessProposal", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property("Address")
.IsRequired()
.HasColumnType("TEXT");
b.Property("Customer")
.IsRequired()
.HasColumnType("TEXT");
b.Property("DateCreated")
.HasColumnType("TEXT");
b.Property("DateDeleted")
.HasColumnType("TEXT");
b.Property("DateUpdated")
.HasColumnType("TEXT");
b.Property("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("BusinessProposals");
});
modelBuilder.Entity("ReportGenerator.Domain.Entities.WorkTask", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property("BusinessProposalId")
.HasColumnType("TEXT");
b.Property("DateCreated")
.HasColumnType("TEXT");
b.Property("DateDeleted")
.HasColumnType("TEXT");
b.Property("DateUpdated")
.HasColumnType("TEXT");
b.Property("Quantity")
.HasColumnType("INTEGER");
b.Property("UnitCost")
.HasColumnType("TEXT");
b.Property("WorkTaskTypeId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("BusinessProposalId");
b.HasIndex("WorkTaskTypeId");
b.ToTable("WorkTasks");
});
modelBuilder.Entity("ReportGenerator.Domain.Entities.WorkTaskType", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property("Category")
.IsRequired()
.HasColumnType("TEXT");
b.Property("DateCreated")
.HasColumnType("TEXT");
b.Property("DateDeleted")
.HasColumnType("TEXT");
b.Property("DateUpdated")
.HasColumnType("TEXT");
b.Property("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property("LaborId")
.HasColumnType("INTEGER");
b.Property("UnitCost")
.HasColumnType("TEXT");
b.Property("UnitType")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("WorkTasksTypes");
});
modelBuilder.Entity("ReportGenerator.Domain.Entities.WorkTask", b =>
{
b.HasOne("ReportGenerator.Domain.Entities.BusinessProposal", "BusinessProposal")
.WithMany("WorkTasks")
.HasForeignKey("BusinessProposalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ReportGenerator.Domain.Entities.WorkTaskType", "WorkTaskType")
.WithMany("WorkTasks")
.HasForeignKey("WorkTaskTypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("BusinessProposal");
b.Navigation("WorkTaskType");
});
modelBuilder.Entity("ReportGenerator.Domain.Entities.BusinessProposal", b =>
{
b.Navigation("WorkTasks");
});
modelBuilder.Entity("ReportGenerator.Domain.Entities.WorkTaskType", b =>
{
b.Navigation("WorkTasks");
});
#pragma warning restore 612, 618
}
}
}
Thats all information i can provide to this case. Can someone help?
Подробнее здесь: https://stackoverflow.com/questions/788 ... nt-failure