Estou trabalhando em um projeto e há um treinador que sigo. Quando cheguei ao processo de atualização, comecei a obter um erro, quando fui com Breakpoints, percebi que o valor QuizId veio 0000 na fase de salvamento. Quando verifico o banco de dados, todas as minhas conexões estão corretas, os valores de id também estão disponíveis, mas tenho um problema com a saída.
Perguntas.cs
[Key]
public Guid Id { get; set; }
public string Text { get; set; }
public Guid QuizId { get; set; }
[ForeignKey(nameof(QuizId))]
public virtual Quiz Quiz { get; set; }
public virtual ICollection<Options> Options { get; set; } = [];
Opções.cs
[Key]
public int Id { get; set; }
public string Text { get; set; }
public bool IsCorrect { get; set; }
public Guid QuestionId { get; set; }
[ForeignKey(nameof(QuestionId))]
public virtual Questions Questions { get; set; }
Questionário.cs
[Key]
public Guid Id { get; set; }
public string? Name { get; set; }
public int TotalQuestions { get; set; }
public int TimeInMinutes { get; set; }
public bool IsActive { get; set; }
public Guid CategoryId { get; set; }
[ForeignKey(nameof(CategoryId))]
public virtual Category? Category { get; set; }
public ICollection<Questions> Questions { get; set; } = [];
abaixo está o bloco de código que usei para salvar os dados
public async Task<QuizApiResponse> SaveQuizAsync(QuizSaveDto dto)
{
var questions = dto.Question.Select(q => new Questions
{
Id = Guid.NewGuid(),
Text = q.Text,
Options = q.Option.Select(o => new Options
{
Id = 0,
Text = o.Text,
IsCorrect = o.IsCorrect
}).ToArray()
}).ToArray();
if (dto.Id == Guid.Empty)
{
var quiz = new Quiz
{
Id = Guid.NewGuid(),
Name = dto.Name,
CategoryId = dto.CategoryId,
TotalQuestions = dto.TotalQuestions,
TimeInMinutes = dto.TimeInMinutes,
IsActive = dto.IsActive,
Questions = questions
};
_context.Quizzes.Add(quiz);
}
else
{
var dbQuiz = await _context.Quizzes.FirstOrDefaultAsync(q => q.Id == dto.Id);//QuizId have
if (dbQuiz == null)
{
return QuizApiResponse.Fail("Quiz doesn't exists");
}
dbQuiz.CategoryId = dto.CategoryId;
dbQuiz.IsActive = dto.IsActive;
dbQuiz.Name = dto.Name;
dbQuiz.TimeInMinutes = dto.TimeInMinutes;
dbQuiz.TotalQuestions = dto.TotalQuestions;
dbQuiz.Questions = questions;//QuizId is 0000
_context.Quizzes.Update(dbQuiz);// burada QuizId null
}
try
{
await _context.SaveChangesAsync();
return QuizApiResponse.Success();
}
catch (Exception ex)
{
return QuizApiResponse.Fail(ex.Message);
}
}
aqui está um visual do meu esquema de banco de dados
o ponto que detectei quando prossegui com o ponto de interrupção
o erro que recebo é o seguinte
Microsoft.EntityFrameworkCore.Update[10000] Ocorreu uma exceção no banco de dados ao salvar alterações para o tipo de contexto 'BlazingQuiz.Api.Data.Repositories.QuizContext'. Microsoft.EntityFrameworkCore.DbUpdateException: Ocorreu um erro ao salvar as alterações da entidade. Consulte a exceção interna para obter detalhes. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): A instrução MERGE entrou em conflito com a restrição FOREIGN KEY "FK_Options_Questions_QuestionId". O conflito ocorreu no banco de dados "BlazingQuiz", tabela "dbo.Questions", coluna 'Id'. em Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Ação
1 wrapCloseInAction) at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction) em Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
O problema provavelmente está em torno de como você está fazendo sua atualização. Para começar, quando você tem uma propriedade de navegação de coleção, você nunca deve expor ou usar um setter. Por exemplo, com Quiz.Questions, isso deve ser:
Nenhum setter. Qualquer código que use um setter causará problemas. Quando você carrega um quiz existente para atualizar as perguntas, não pode simplesmente substituir a referência da coleção por um novo conjunto, como pode com coleções na memória. O quiz tem perguntas atribuídas a ele, então você precisa determinar quais itens adicionar ou remover, ou remover todos e adicionar completamente novos, desde que a nova lista de perguntas tenha IDs completamente novos e exclusivos. Definir uma coleção de propriedades de navegação causa todos os tipos de problemas porque remove o proxy de rastreamento que o EF teria ao ler a coleção e não sabe depois do fato "deletar a coleção inteira" quando vai salvar as alterações depois que essa referência é detonada.
Então, ao carregar o Quiz existente, carregue ansiosamente as perguntas existentes:
A partir daí, para substituir a lista de perguntas por novas:
por fim, para salvar as alterações, não use
Update
.Update
é usado para entidades desanexadas e ignora o rastreamento do rastreador de alterações para modificações. Quando você carrega o Quiz e as perguntas, por padrão, essas são entidades de rastreamento, então tudo o que você precisa fazer é chamarSaveChanges()
depois de fazer suas alterações:O EF gerenciará todas as atribuições de FK para as entidades relacionadas. A única outra etapa que pode ser necessária em termos de tratamento das perguntas órfãs removidas do quiz é que você pode precisar marcá-las explicitamente como excluídas se o relacionamento do banco de dados não estiver configurado para excluir órfãos:
Espero que isso o deixe mais perto de uma solução funcional.