Home > OS >  Inserting multiple entities (one after another, not together), makes the framework try to overwrite
Inserting multiple entities (one after another, not together), makes the framework try to overwrite

Time:10-22

I am using C#, Entity Framework, ASP.NET Core MVC and SQL Server

To summarize, I am using an iteration to insert multiple entities one by one, so I can get the result from the .SaveChangesAsync().

Problem is, it seems that the newly inserted row is not being recognized for the next insertion, which makes the framework, or DB, try to create inserting into the same ID.

foreach (var concessao in Concessoes)
{
    _context.Add(concessao);
    var resultado = await _context.SaveChangesAsync();
}

I cleared some code to make it simpler. I know I can insert multiple entities at once. But I want to get the result for all of them, instead of the result for the whole operation.

Example of what happens (checked using the debugger and DB):

  1. Existing IDs go from 1 to 8.
  2. Trying to insert 2 new Entities
  3. First Entity is mapped to ID 9.
  4. Second Entity is also mapped to ID 9, so I get a:

InvalidOperationException: The property 'ConcessaoCreditoAluno.IdConcessaoCreditoAluno' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal.`

To me, it seems that the First newly inserted Entity is not being recognized before inserting the second one, as if it was not there. Like if i should close the DB and reopen it to get it updated before inserting a new one. But if this is the case, I don't know how to do it.

CodePudding user response:

The error is referring to a "ConcessaoCreditoAluno" which my guess would be an entity referenced within the Concessoa entity(ies) you are trying to save.

These errors commonly occur when dealing with multiple references to the same entity. Check that all instances in the collection and their related instances are new, distinct instances of classes, and that your entity mappings are valid for the type of relationship you want to enforce:

Based on the exception I'd hazard a guess that the two Concessoa records (A and B) are both referencing the same instance of a ConcessaoCreditoAluno. However, EF is interpreting this relationship as being that the ConcessaoCreditoAluno relationship is exclusive and the second save is attempting to change a Key value.

Possible causes that come to mind might be a mismatched foreign key relationship where a CocessaoCreditoAlunoId is being mapped to a ConcessoaId in the the Concessoe table. This could occur if EF is configured to expect this to be a One-to-One relationship without an explicit FK mapped on one side or the other. (I.e. Concessoe has a ConcessoCreditoAlunoId for the FK or CocessoaCreditoAluno has a ConcessoaId for the FK) If the two Concessao

For example, this demonstrates the issue:

var concessaoCreditoAluno = new ConcessaoCreditoAluno {Name = "Test"};
var concessoaA = new Concessoa {Name = "A", ConcessaoCreditoAluno = concessaoCreditoAluno};
var concessoaB = new Concessoa {Name = "B", ConcessaoCreditoAluno = concessaoCreditoAluno};

context.Concessoes.Add(concessoaA);
context.SaveChanges();
context.Concessoes.Add(concessoaB);
context.SaveChanges();

The issue here is that both A and B reference the same instance of the CreditoAluno. When "A" gets saved, the DB assigns it's ConcessoaId to "8", and the CreditorAluno's ID to match. All is good. However, when it goes to save "B", it assigns B's ConcessoaId to "9" and tries to update the associated Aluno's "Key" to match, resulting in an error that it's trying to alter a tracked entity's key. It is referencing the same instance.

The solution is either that if this is truly a one-to-one relationship where both Concessoa reference a distinct CreditoAluno then you need to ensure these are separate references:

var concessaoCreditoAlunoA = new ConcessaoCreditoAluno {Name = "Test"};
var concessaoCreditoAlunoB = new ConcessaoCreditoAluno {Name = "Test"};
var concessoaA = new Concessoa {Name = "A", ConcessaoCreditoAluno = concessaoCreditoAlunoA};
var concessoaB = new Concessoa {Name = "B", ConcessaoCreditoAluno = concessaoCreditoAlunoB};

context.Concessoes.Add(concessoaA);
context.SaveChanges();
context.Concessoes.Add(concessoaB);
context.SaveChanges();

If the ConcessaoCreditoAluno can be shared between Concessoa instances then the mapping needs to be changed from a One-to-One to a Many-to-One, where the Concessoa would need to have a ConcessaoCreditoAlunoId mapped, with a HasOne().WithMany() mapping.

CodePudding user response:

It is always bad idea to add to the whole context. Try to add to dbset, and it is better to add range instead of using loop

_context.Set<Concessoe>().AddRange(Concessoes);
 var resultado = await _context.SaveChangesAsync();
  • Related