Reputation: 27
An exception was thrown when I was trying to update an entry in the database. First I get the entry from the db in order to retrieve record date of the entry. Then I migrate dbmodel to datamodel, then migrate it back. But it did not work.
Attaching an entity of type 'ProgramLanguage' 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.
This is my dbmodel
public partial class ProgramLanguage
{
public int ProgramLanguageId { get; set; }
public int TestId { get; set; }
public string ProgramLanguageName { get; set; }
public string ProgramLanguageLevel { get; set; }
public Nullable<System.DateTime> RecordDate { get; set; }
public virtual Test Test { get; set; }
}
This is my data model
[DataContract]
[Serializable]
public class ProgramLanguage
{
public ProgramLanguage()
{
Test = new Test();
}
[DataMember]
public int ProgramLanguageId { get; set; }
[DataMember]
public int TestId { get; set; }
[DataMember]
public string ProgramLanguageName { get; set; }
[DataMember]
public string ProgramLanguageLevel { get; set; }
[DataMember]
public DateTime RecordDate { get; set; }
[DataMember]
public Test Test { get; set; }
}
This is the part of code that interacts with db context.
public bool UpdateProgramLanguage(ProgramLanguage programLanguage)
{
_repository.Update(programLanguage);
return true;
}
This is the business layer that talks to the db module.
public bool UpdateProgramLanguage(ProgramLanguageDm programLanguage)
{
using (var iUnitOfWork = new UnitOfWork<TourismEntities>())
{
var coreProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId);
if (coreProgramLanguage == null)
{
return false;
}
var detachedCore = GeneralMigrate.ProgramLanguageDmToEntity(GeneralMigrate.ProgramLanguageEntityToDm(coreProgramLanguage));
var newCore = GeneralMigrate.ProgramLanguageDmToEntity(programLanguage);
newCore.RecordDate = detachedCore.RecordDate;
newCore.TestId = detachedCore.TestId;
return iUnitOfWork.CommonHandler.UpdateProgramLanguage(newCore);
}
}
This is the part of code when I am migrating model to datamodel.
public static ProgramLanguageDm ProgramLanguageEntityToDm(ProgramLanguage entity)
{
return entity != null ? new ProgramLanguageDm{
TestId = entity.TestId,
ProgramLanguageId = entity.ProgramLanguageId,
ProgramLanguageLevel = entity.ProgramLanguageLevel,
ProgramLanguageName = entity.ProgramLanguageName,
RecordDate = entity.RecordDate ?? new DateTime(),
}
: new ProgramLanguageDm();
}
public static ProgramLanguage ProgramLanguageDmToEntity(ProgramLanguageDm dm)
{
return dm != null ? new ProgramLanguage {
TestId = dm.TestId,
ProgramLanguageId = dm.ProgramLanguageId,
ProgramLanguageLevel = dm.ProgramLanguageLevel,
ProgramLanguageName = dm.ProgramLanguageName
}
: new ProgramLanguage();
}
Upvotes: 1
Views: 88
Reputation: 34968
PiGi78 nailed the issue: You are reading and converting between data model and entity excessively.
The only time you should be converting Dm to Entity would be when creating a new entity. When updating an entity we can load the entity, copy the values across from the passed model and save the entity. (No need to convert things back and forth)
This code:
var coreProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId);
if (coreProgramLanguage == null)
{
return false;
}
var detachedCore = GeneralMigrate.ProgramLanguageDmToEntity(GeneralMigrate.ProgramLanguageEntityToDm(coreProgramLanguage));
var newCore = GeneralMigrate.ProgramLanguageDmToEntity(programLanguage);
newCore.RecordDate = detachedCore.RecordDate;
newCore.TestId = detachedCore.TestId;
return iUnitOfWork.CommonHandler.UpdateProgramLanguage(newCore);
... can be simplified considerably. I've renamed a few of the variables for clarity.
var existingProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId);
if (coreProgramLanguage == null)
return false;
existingProgramLanguage.RecordDate = programLanguage.RecordDate;
return iUnitOfWork.CommonHandler.UpdateProgramLanguage(existingProgramLanguage);
// assuming UpdateProgramLanguage returns Boolean? if it returns entity then convert to data model.
rather than reading the "detached" existing record and converting it to a DM and converting the passed DM into a new entity, we load the existing record and copy the allowed values from our passed DM into it and save it.
Upvotes: 1
Reputation: 435
You have this error because you're trying to attach/add an object to a database context when the context already knows an entity with the same Id.
Let me do a simple example for explain it in a easier way.
We have those model and dto:
public class MyModel
{
[Key]
public int Id { get; set; }
[StringLength(30)]
public string Description { get; set; }
}
public class MyDTO
{
public int Id { get; set; }
public string Description { get; set; }
}
Not a lot of properties: an Id (primary key) and a description.
Now, I do what you do in yor code:
using (var db = new MyDbContext())
{
// In the DB context, there is 1 object with id 1
var model = await db.MyModels.SingleOrDefaultAsync(x => x.Id == 1);
// Convert model into DTO
var dto = new MyDTO { Id = model.Id, Description = model.Description };
// Convert back to a new model (you do that in your code)
var anotherModel = new MyModel { Id = dto.Id, Description = dto.Description };
// Trying to add the model to the db context...
// It will throw an exception since the db context already has an object with Id = 1 (model)
// The exception is the same you posted
db.MyModels.Attach(anotherModel);
}
Looking at all steps:
When we do the 4th step, the application throws an exception, since the DbContext already know a model with Id = 1 (variable model) and you can't add another one with the same id (anotherModel).
There are two options:
Looking at your code, I suggest to use the first options. It will be easyer and, since it hasn't to take care of the tracking, the query will be faster.
I hope I helped
Upvotes: 3