c# - ICollectionView 抛出 Entity Framework 附加异常

标签 c# entity-framework mvvm prism valueinjecter

当我尝试将对象保存到 EF 时,它会抛出此异常:

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
如果我取出 'cliItems = new ListCollectionView(t.ToList());'它运行完美,但是我需要将 ListCollectionView 用于 PRISM 模式。
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; }
}
我的服务(业务逻辑)类:
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;
    }
}
在我的 Repository.cs 里面有这个:
public virtual void Update(TEntity entity)
    {
        ((IObjectState)entity).ObjectState = ObjectState.Modified;
        _dbSet.Attach(entity);// EXCEPTION HERE
        _context.SyncObjectState(entity);
    }
我正在使用 Generic Unit of Work & (Extensible) Repositories Framework
对于我的存储库层。
用于 ViewModel 和实体之间的映射,使用 Value Injecter
还有我的项目的图像(它是桌面 + UNITY + Prism 模块)
My project
更新:
如何重现它:
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);
更新已解决:
使用上述回答者的评论解决了问题。
Repository.cs 有一个内部 IQueryable Select(.... 我在这一行添加了 AsNoTracking() :
IQueryable<TEntity> query = _dbSet.AsNoTracking();
现在,当我使用以下方法更新我的对象时:
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);
        }

最佳答案

我真的不知道如何创建上下文/存储库/服务,如果在保存更改后正确处理上下文并在每个新操作中创建一个新的,这应该不是问题,因为 Local缓存总是新的。

异常表示存在一个具有相同 ID 的现有实体,该实体已附加到 Local。缓存,您不能附加具有相同 Id 的另一个实体,您需要先分离现有实体。

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

_dbSet.Attach(entity);// EXCEPTION HERE

更新

另一种选择是覆盖 SaveChanges并在保存修改后的实体后分离它们。
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;
}

更新2

DbSet<T> 检索项目也可能导致异常。然后使用相同的键附加另一个不同的实体,默认情况下将跟踪(附加)这些项目。可以通过提及 AsNoTracking 来禁用它.

这是一个简单的错误,在检索项目时会导致错误。
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);

除非AsNoTracking被指定。
var itemDb = db.Set<Entity>().AsNoTracking().First(x => x.Id == 324);

关于c# - ICollectionView 抛出 Entity Framework 附加异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25454801/

相关文章:

c# - 使用 Visual Studio 连接 MySQL 数据库

c# - 存储函数以供稍后运行

C# DateTime.UtcNow 返回错误值

c# - 在 Aspnet Core 中加载元数据

ListBox 内的 WPF ComboBox 文本

c# - EF 更新不更新 GridView

c# - 在不使用任何内置函数的情况下在 .NET 中生成随机数

entity-framework - Entity Framework 迁移: Prevent dropping column or table

c# - 使用 EF Core 处理 MySQL 零日期

数据绑定(bind)属性更新的 WPF 平滑过渡