ICollectionView throw实体框架附加异常

时间:2014-08-22 20:03:54

标签: c# entity-framework mvvm prism valueinjecter

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

  

EntityFramework.dll中发生了'System.InvalidOperationException'类型的异常,但未在用户代码中处理。

     

附加信息:附加类型的实体   'Sistema.DataEntities.Models.Cliente'因为另一个实体而失败   相同类型已具有相同的主键值。这可能发生   使用“附加”方法或将实体的状态设置为时   如果图中的任何实体具有“未更改”或“已修改”   冲突的关键值。这可能是因为一些实体是新的和   尚未收到数据库生成的键值。在这种情况下使用   “添加”方法或“已添加”实体状态可跟踪图表和   然后将非新实体的状态设置为“未更改”或“已修改”为   合适的。

Error Exception

如果我拿出'cliItems = new ListCollectionView(t.ToList());'它运行完美,但我需要使用ListCollectionView for 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 对于我的存储库层。

使用Value Injecter

在ViewModel和实体im之间进行映射

我的项目图片(桌面+ 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);
        }

1 个答案:

答案 0 :(得分:1)

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

例外情况说,现有的实体具有相同的ID已附加到Local缓存,您无法附加具有相同ID的另一个实体,您需要分离现有的实体第一。

Local

<强>更新

另一种选择是覆盖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

<强> UPDATE2

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; } 检索项目然后使用相同的密钥附加另一个不同的实体也可能导致异常,默认情况下将跟踪(附加)这些项目。可以通过提及AsNoTracking来禁用它。

这是一个在检索项目时导致错误的简单错误。

DbSet<T>

除非指定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