1

When i try to save a Object to EF it throw this exception:

An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code.

Additional information: Attaching an entity of type 'Sistema.DataEntities.Models.Cliente' 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.

Error Exception

If i take out the 'cliItems = new ListCollectionView(t.ToList());' its runs perfectly however i need to use the ListCollectionView for PRISM patterns.

public class CadastroClienteViewModel : BindableBase, ICadastroClienteViewModel
{
    private readonly IClienteService _clienteService;

    public CadastroClienteViewModel(IClienteService ServiceCliente)
    {
        _clienteService = ServiceCliente;

        this.SaveCommand = new DelegateCommand(ExecuteMethodSave);
        this.RefreshCommand = new DelegateCommand(ExecuteMethodRefresh, CanExecuteMethodRefresh);
        RefreshCommand.Execute(null);
    }

    private void ExecuteMethodSave()
    {
        Sistema.DataEntities.Models.Cliente clifinal = new Sistema.DataEntities.Models.Cliente();

        clifinal.InjectFrom<UnflatLoopValueInjection>(ObCliente);

        _clienteService.ClienteService_Update(clifinal); //EXCEPTION HERE

        RefreshCommand.Execute(null);
    }

    private bool CanExecuteMethodRefresh()
    {
        return true;
    }

    private void ExecuteMethodRefresh()
    {
       //var t = _clienteService.ClienteService_GetAll().ToList(); 
       //var y = t.Select(p => new Cliente().InjectFrom<FlatLoopValueInjection>(p));

        var t = _clienteService.ClienteService_GetAll().Select(p => new Cliente().InjectFrom<FlatLoopValueInjection>(p));

        cliItems = new ListCollectionView(t.ToList());//if i take this line out, no exceptions.
        cliItems.CurrentChanged += cliItemsOnCurrentChanged;

        OnPropertyChanged("cliItems");
    }

    private void cliItemsOnCurrentChanged(object sender, EventArgs eventArgs)
    {
        ObCliente = (Cliente)cliItems.CurrentItem;
        this.OnPropertyChanged("ObCliente");
    }
    public ICommand SaveCommand { get; private set; }
    public ICommand RefreshCommand { get; private set; }
    public Cliente ObCliente { get; private set; }
    public ICollectionView cliItems { get; private set; }
}

My Service (Business Logic) Class:

public class ClienteService : Common.Services.Service<Cliente>, IClienteService
{
    private readonly IRepositoryAsync<Cliente> _repository;
    private readonly IUnitOfWorkAsync _uow;

    public ClienteService(IRepositoryAsync<Cliente> repository, IUnitOfWorkAsync uow)
        : base(repository)
    {
        _repository = repository;
        _uow = uow;
    }

    public void ClienteService_Adicionar(Cliente Obcliente)
    {
        _repository.Insert(Obcliente);
        _uow.SaveChanges();
    }

    public void ClienteService_Update(Cliente Obcliente)
    {
        Obcliente.ObjectState = ObjectState.Modified;
        _repository.Update(Obcliente);//HERE THE EXCEPTION
        _uow.SaveChanges();
    }

    public IEnumerable<Cliente> ClienteService_GetAll()
    {
        var t = _repository.Query().Select().AsEnumerable();
        return t;
    }
}

Inside of my Repository.cs o have this:

public virtual void Update(TEntity entity)
    {
        ((IObjectState)entity).ObjectState = ObjectState.Modified;
        _dbSet.Attach(entity);// EXCEPTION HERE
        _context.SyncObjectState(entity);
    }

Im using the Generic Unit of Work & (Extensible) Repositories Framework For my repository layer.

For Mapping between ViewModels and Entity im using Value Injecter

And a image of my project (Its a desktop + UNITY + Prism modules)

My project

UPDATE:

How to reproduce it:

IEnumerable<Cliente> Clientes = _clienteService.ClienteService_GetAll();

var personViewModels = new List<Sistema.MVVMModels.CadastroModule.Cliente>().InjectFrom(Clientes);

Sistema.MVVMModels.CadastroModule.Cliente cliConvertido = personViewModels.SingleOrDefault(x => x.ClienteID == 1);

//cliConvertido.InjectFrom<SmartConventionInjection>(obCliente);

cliConvertido.Nome = "A" + rand.Next(999999, 9999999) + " BREDA";

Cliente obCliente = new Cliente();

obCliente.InjectFrom<SmartConventionInjection>(cliConvertido);

_clienteService.ClienteService_Update(obCliente);

UPDATE RESOLVED:

Problem resolved using the comment of the answere above.

The Repository.cs has a internal IQueryable Select(.... I added the AsNoTracking() on this line:

IQueryable<TEntity> query = _dbSet.AsNoTracking();

Now when i update my object using:

public virtual void Update(TEntity entity)
        {
            var existing = _dbSet.Local;// NOW LOCAL IS NULL 

            entity.ObjectState = ObjectState.Modified;
            _dbSet.Attach(entity);//no exceptions here
            _context.SyncObjectState(entity);
        }
Community
  • 1
  • 1
Danilo Breda
  • 1,092
  • 1
  • 15
  • 30
  • Is `Insert` method used to insert or update? the code inside it in first line it seems an insert operation, but the next code is attaching to the context? And does the service live in the same application domain with the client (or the service is actually hosted on different application domain (windows service / iis)) ? Is the context instance ever disposed / closed ? – Yuliam Chandra Aug 22 '14 at 20:42
  • Wrong method, now its correct... updated – Danilo Breda Aug 22 '14 at 20:46
  • is the context instance singleton (the only one as long as the domain lives) ? Is it ever closed / disposed ? – Yuliam Chandra Aug 22 '14 at 20:49
  • Yes... how manage it is the unity by UnityOfWork. The disposes are very managed inside of the Repository framework that im using. This error can have some relative to this repository framework? public virtual void Update(TEntity entity) is from this framework. – Danilo Breda Aug 22 '14 at 20:54
  • 1
    You might have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:37

1 Answers1

1

I'm not really aware how the creation of the context / repository / service, if the context is disposed properly after saving the changes and creates a new one every new operation, it should not be a problem since the Local cache is always new.

And the exception says that there is an existing entity with the same Id that has been attached to the Local cache, you can't attach another entity with the same Id, you need to detach the existing entity first.

var existing = _dbSet.Local.FirstOrDefault(x => x.Id == entity.Id);
if (existing != null)
    _context.Entry(existing).State = EntityState.Detached;

_dbSet.Attach(entity);// EXCEPTION HERE

update

Another alternative would be overriding the SaveChanges and detach the modified entities once they are saved.

public override int SaveChanges()
{
    var modifiedEntities = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Modified).ToArray();
    var rowsAffected = base.SaveChanges();
    foreach (var entity in modifiedEntities)
        entity.State = EntityState.Detached;
    return rowsAffected;
}

update2

The exception could also be caused by retrieving items from the DbSet<T> then attach another different entity with the same key, by default those items will be tracked (attached). It can be disabled by mentioning AsNoTracking.

Here is a simple error that cause error when retrieving items.

Entity item = new Entity { Id = 324 };

// itemDb is automatically attached.
var itemDb = db.Set<Entity>().First(x => x.Id == 324);
// Attaching another different entity (different reference)
//   with the same key will throw exception.
db.Set<Entity>().Attach(entity);

Unless AsNoTracking is specified.

var itemDb = db.Set<Entity>().AsNoTracking().First(x => x.Id == 324);
Yuliam Chandra
  • 14,494
  • 12
  • 52
  • 67
  • 1
    i talked to the creator of Generic Unit of Work & (Extensible) Repositories Framework, he told me that the problem was fixed 1 week ago... and i will download the next version to test it. Thanks for help. – Danilo Breda Aug 24 '14 at 17:17
  • @Daniloloko, in their [repository test example](https://genericunitofworkandrepositories.codeplex.com/SourceControl/latest#main/Sample/Northwind.Test/IntegrationTests/CustomerRepository_Tests.cs), the context and unit of work are instantiated each operation (short lived context), if you want to have a long lived context, you need to do as I explained before, I added another alternative by overriding the save changes to detach the modified entities after the changes are saved. – Yuliam Chandra Sep 02 '14 at 04:13
  • I dont thiink thats the problem because the Local is not cleaned everytime... i debug it... please see the update on how to reproduce it. – Danilo Breda Sep 02 '14 at 13:06
  • 1
    @Daniloloko, your updated code is caused by the tracked entities when retrieving `Cliente` from `_clienteService.ClienteService_GetAll();`, which it can be simplified as [this example](https://dotnetfiddle.net/zUs9VU) and it can be solved by introducing [`AsNoTracking`](http://stackoverflow.com/questions/12211680/what-difference-does-asnotracking-make), and since the framework wraps the `DbSet`, you can't use it, I think this framework doesn't fit for windows application, unless you change the way you instantiate the `IClienteService` into per operation (per method call) – Yuliam Chandra Sep 02 '14 at 14:16
  • Thanks for the back message... you are helping me a lot... I think this framework really doesn't fit for windows application... im trying to talk to the framework owner... but the back message is taking time.To add the AsNoTracking... i need to update the Repository framework... right? Thats the problem... – Danilo Breda Sep 02 '14 at 15:07
  • Please add the comments aboud AsNoTracking on your answere to be more easy to somebody found the solution. Thanks... Problem resolved. – Danilo Breda Sep 03 '14 at 17:52