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.
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)
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);
}