Base solution for your next web application
Open Closed

Updating Enity after retrieving list of same type #651


User avatar
0
corneduplooy created

Hi,

Im running into the following error when trying to update an entity:

Attaching an entity of type 'ITFM.CostAccount' 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 started happening after moving my code from Application Service (*.Application project) to a Domain Service (ObjectManager in *.Core project).

I have noticed though that this only happens after a retrieve a list of the stored objects before trying to update the object passed into the function as a DTO.

So to make it a bit clearer, a simple version of my code is

public async Task<TaskResult> UpdateDto(CostAccountDto value) {

List<CostAccount> storedAccounts = await _costAccountRepository.GetAllListAsync() //IRepository<CostAccount> using Dependency Injection

await _costAccountRepository.Update(value.MapTo<CostAccount>()); }

If I do not call the GetAllListAsync function on the repository, the update commits without an error.

Can you assist in resolving this.

REgards, Corne du Plooy


6 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    I did not understand it. It seems something related to EF, rather than ABP. Are you using UpdateDto method of the domain service from an application service? Can you try to add [UnitOfWork] to UpdateDto method and make UpdateDto method virtual.

  • User Avatar
    0
    corneduplooy created

    HI hikalkan,

    Thank you for the reply.

    I have created a sample that shows how the error is generated (Not using a Domain Service but only an Application service)

    public class TestAppService : ITestAppService
        {
            private IRepository<CostAccount> _costAccountRepository;
    
            public TestAppService(IRepository<CostAccount> costAccountRepository)
            {
                _costAccountRepository = costAccountRepository;
            }
    
            [UnitOfWork]
            public virtual async Task<bool> UpdateTest(List<CostAccountDto> values)
            {
                foreach (CostAccountDto I in values)
                {
                    await UpdateTest(I);
                }
                return true;
            }
    
            [UnitOfWork]
            public virtual async Task<bool> UpdateTest(CostAccountDto value)
            {
                //Get list of stored Cost Accounts
                List<CostAccount> storedCostAccounts = await _costAccountRepository.GetAllListAsync();//This is required for business rules validation which has been excluded for simplicity.
                await _costAccountRepository.UpdateAsync(value.MapTo<CostAccount>());
                return true;
            }
        }
    

    The EF error only occurs when I retrieve all the stored objects before calling the update method on the repository.

    Regards, Corne du Plooy

  • User Avatar
    0
    hikalkan created
    Support Team

    You should always get an entity before updating it.

    Change this:

    await _costAccountRepository.UpdateAsync(value.MapTo<CostAccount>());
    

    To this:

    var account = await _costAccountRepository.GetAsync(value.Id);
    value.MapTo(account);
    

    This is best practice of updating an entity. Always do it like that. Thus, you don't break your code if you add a new property to CostAccount in the future and not add to CostAccountDto.

    Note: You don't even need to call UpdateAsync (because UOW automatically save changes).

  • User Avatar
    0
    corneduplooy created

    Thanx a stack!

    I will give that a try :)

    Regards, Corne du Plooy

  • User Avatar
    0
    corneduplooy created

    Just want to post an update for anyone stumbling across this thread...

    For the MapTo extension method to work on an entity where you want to MapTo an Object of the same type, you still need to decorate the class with the AutoMap annotation:

    [AutoMap(typeof(MyObject))]
    public class MyObject: FullAuditedEntity, IMustHaveTenant
    {
    }
    
  • User Avatar
    0
    hikalkan created
    Support Team

    This is behaviour of AutoMapper. Because MapTo and AutMap attributes are just simple wrappers.

    FYI.