Error -another entity of the same type already has the same primary key value

Copper Contributor

Good afternoon,

I have an error, and I think you can help me.

  • Error
    in Microsoft Visual Studio (Microsoft .NET Framework Version 4.8, EntityFramework 6.2):

"
System.InvalidOperationException
HResult=0x80131509
Message=Attaching an entity of type 'SGI2DataAccess.Models.ProjectForecast' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
Source=EntityFramework
StackTrace:
at System.Data.Entity.Core.Objects.ObjectContext.VerifyRootForAdd(Boolean doAttach, String entitySetName, IEntityWrapper wrappedEntity, EntityEntry existingEntry, EntitySet& entitySet, Boolean& isNoOperation)
at System.Data.Entity.Core.Objects.ObjectContext.AttachTo(String entitySetName, Object entity)
at System.Data.Entity.Internal.Linq.InternalSet`1.<>c__DisplayClassa.<Attach>b__9()
at System.Data.Entity.Internal.Linq.InternalSet`1.ActOnSet(Action action, EntityState newState, Object entity, String methodName)
at System.Data.Entity.Internal.Linq.InternalSet`1.Attach(Object entity)
at System.Data.Entity.DbSet`1.Attach(TEntity entity)
at SGI2.Data.Infrastructure.RepositoryBase`1.Update(T entity) in C:\work\sgi\SGI2.Data\Infrastructure\RepositoryBase.cs:line 41
at SGI2.Service.Services.Projects.ForecastProject.UpdateForecast(List`1 forecasts, ProjetoVM Projeto) in C:\work\sgi\SGI2.Service\Services\Projects\ForecastProject.cs:line 79
at SGI2.Service.ProjetosService.UpdateProjeto(ProjetoEditVM VM, Int32 userId, String userNomeConhecido) in C:\work\sgi\SGI2.Service\Services\Projects\ProjetosService.cs:line 1212
at SGI2.Controllers.ProjetosController.Edit(ProjetoEditVM VM, String Command) in C:\work\sgi\SGI2\SGI2\Controllers\Projeto\ProjetosController.cs:line 312
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()

"

  • Code:

object: \sgi2\SGI2\Controllers\Projeto\ProjetosController.cs
(...)
// Interface
private readonly IProjetosService _service;
(...)

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ProjetoEditVM VM, string Command)
{
#region PERMISSOES
if (!ClaimsAuthorization.CheckAccess("generic-editprojetos", VM.Projeto.Id.ToString(), VM.Projeto.EstadoId.ToString()))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);

// Completed projects should never be able to be edited
if (_service.GetProjeto(VM.Projeto.Id).EstadoId == 2)
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);

#endregion
else if (ModelState.IsValid)
{
if (VM.Projeto.EstadoId == 2 && (VM.Projeto.TipoId == 8 || VM.Projeto.TipoId == 9) && !_service.GetAllProjetosPPRInfo(x => x.ProjetoId == VM.Projeto.Id && x.IsTheLast).Any())
{
return RedirectToAction("Edit", new { Id = VM.Projeto.Id, param = "CloseProjectLastPPRInfoError" });
}


// Se vem do botão "Refresh Forecast", atualizar valores, antes de submeter com valores desatualizados
if (Command == "RefreshForecastButton")
{
_service.UpdateProjetoRF(VM, UserId, UserNomeConhecido); // ok
RefreshForecastButton(VM, Command, UserId, UserNomeConhecido);
_service.UpdateProjeto(VM, UserId, UserNomeConhecido); // NOT ok
}
(...)
} (...)

}
(...)

object: \sgi\SGI2.Service\Services\Projects\ProjetosService.cs
public void UpdateProjeto(ProjetoEditVM VM, int userId, string userNomeConhecido)
{
//Atualiza projeto

var dbObj = projetosRepository.GetById(VM.Projeto.Id);
(...)
new ForecastProject(projectForecastRepository, projetoPPRInfoRepository).UpdateForecast(VM.Forecasts, VM.Projeto);
(...)
unitOfWork.Commit();

}

object: \sgi\SGI2.Service\Services\Projects\ForecastProject.cs
namespace SGI2.Service.Services.Projects
{
//Tenho de criar uma interface e remover a dependecia dos repositórios
public class ForecastProject
{

private readonly IProjectForecastRepository projectForecastRepository;
private readonly IProjetoPPRInfoRepository projetoPPRInfoRepository;
private readonly IForecastDomain Iforecast = new ForecastDomain();


public ForecastProject(IProjectForecastRepository projectForecastRepository, IProjetoPPRInfoRepository projetoPPRInfoRepository)
{
this.projectForecastRepository = projectForecastRepository;
this.projetoPPRInfoRepository = projetoPPRInfoRepository;
}
(...)

public void UpdateForecast(List<ProjectForecastEditVM> forecasts, ProjetoVM Projeto)
{
// 1º Caso não exista linhas
if (forecasts == null || forecasts.Count() == 0)
{
foreach (var item in Iforecast.InitialLinesForecast(Projeto))
{
projectForecastRepository.Add(item);
}
}

// 2º caso a data de fim do projeto aumente
else if (Projeto.RealEndDate > forecasts.LastOrDefault().EndDate)
{
foreach (var item in Iforecast.ChangeRealEndDateForecast(Projeto,forecasts.LastOrDefault().EndDate))
{
projectForecastRepository.Add(item);
}
}

else
{
List<ProjectForecast> Forecasts = new List<ProjectForecast>();
foreach (var i in forecasts)
{
//Está assim pois a data fim pode mudar e deixar de ser o ultimo dia do mês
DateTime EndDate = new DateTime(i.EndDate.Year, i.EndDate.Month, DateTime.DaysInMonth(i.EndDate.Year, i.EndDate.Month));
//Ultimo dia do mês do projeto
DateTime LastDateofProject = new DateTime(Projeto.RealEndDate.Value.Year, Projeto.RealEndDate.Value.Month, DateTime.DaysInMonth(Projeto.RealEndDate.Value.Year, Projeto.RealEndDate.Value.Month));

if (EndDate > LastDateofProject && i.CanWrite == true)
{
projectForecastRepository.Delete(projectForecastRepository.GetById(i.Id));
}
else
{
Forecasts.Add(UpdateForecastForecastFactory(Projeto, i));
}
}

foreach (var item in Forecasts)
{
projectForecastRepository.Update(item); ! The error is thrown here !
}
}
}

}


object: \SGI2.Data\Infrastructure\IRepository.cs
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace SGI2.Data.Infrastructure
{
public interface IRepository<T> where T : class
{
// Marks an entity as new
void Add(T entity);
// Marks an entity as modified
void Update(T entity);
// Marks an entity to be removed
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
// Get an entity by int id
T GetById(int id);
// Get an entity using delegate
T Get(Expression<Func<T, bool>> where);
// Gets all entities of type T
IEnumerable<T> GetAll();
// Gets entities using delegate
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);


}
}

object: \SGI2.Data\Infrastructure\RepositoryBase.cs
public virtual void Update(T entity)
{
dbSet.Attach(entity); ! The error is thrown here !
dataContext.Entry(entity).State = EntityState.Modified;
}


What can I do to resolve? What i want is call twice :_service.UpdateProjeto

Best regards
Isabel Fonseca

3 Replies
The situation was resolved.
public virtual void Update(T entity, int id) {
var aExists = dbSet.Find(id);
if (aExists == null) {
dbSet.Add(entity);
} else {
dataContext.Entry(aExists).State = EntityState.Detached; dbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
}

Thanks.
Thank you very much, it helped me a lot