ICollectionView抛出实体框架附加异常 [英] ICollectionView throw Entity Framework Attach exception

查看:81
本文介绍了ICollectionView抛出实体框架附加异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


在EntityFramework.dll中发生了类型为'System.InvalidOperationException'的异常,但是


其他信息:附加类型为
'Sistema.DataEntities.Models.Cliente'的实体失败,因为另一个
实体相同类型已经具有相同的主键值。当使用附加方法或将实体的状态设置为
不变或修改时,如果图中的任何实体具有键值相冲突的
,则可能发生
。这可能是因为某些实体是新实体,并且
尚未收到数据库生成的键值。在这种情况下,请使用
的'Add'方法或'Added'实体状态来跟踪图形,然后
将非新实体的状态设置为$ b $来设置为'Unchanged'或'Modified' b合适。



如果我取出'cliItems = new ListCollectionView(t.ToList());',它运行得很好,但是我需要对PRISM模式使用ListCollectionView。

 公共类CadastroClienteViewModel:BindableBase,ICadastroClienteViewModel 
{
私有只读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); //此处的例外

RefreshCommand.Execute(null);
}

私人布尔值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()); //如果我将这一行删除,也没有例外。
cliItems.CurrentChanged + = cliItemsOnCurrentChanged;

OnPropertyChanged( cliItems);
}

private void cliItemsOnCurrentChanged(对象发送者,EventArgs eventArgs)
{
ObCliente =(Cliente)cliItems.CurrentItem;
this.OnPropertyChanged( ObCliente);
}
public ICommand SaveCommand {get;私人套装; }
public ICommand RefreshCommand {get;私人套装; }
public Cliente ObCliente {get;私人套装; }
public ICollectionView cliItems {get;私人套装; }
}

我的服务(业务逻辑)类别:

 公共类ClienteService:Common.Services.Service< Cliente> ;, IClienteService 
{
私有只读IRepositoryAsync< Cliente> _repository;
私有只读IUnitOfWorkAsync _uow;

public ClienteService(IRepositoryAsync< Cliente>存储库,IUnitOfWorkAsync uow)
:基本(存储库)
{
_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); //这里例外
_uow.SaveChanges();
}

公共IEnumerable< Cliente> ClienteService_GetAll()
{
var t = _repository.Query()。Select()。AsEnumerable();
返回t;
}
}

在我的Repository.cs中,有以下内容:

  public virtual void Update(TEntity实体)
{
((IObjectState)entity).ObjectState = ObjectState.Modified;
_dbSet.Attach(entity); //例外情况
_context.SyncObjectState(entity);
}

我使用。



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

 实体项目=新实体{ID = 324}; 

// itemDb自动附加。
var itemDb = db.Set< Entity>()。First(x => x.Id == 324);
//使用相同的键附加另一个不同的实体(不同的引用)
//将引发异常。
db.Set< Entity>()。Attach(entity);

除非指定了 AsNoTracking

  var itemDb = db.Set< Entity>()。AsNoTracking()。First(x => x.Id == 324) ; 


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

解决方案

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

这篇关于ICollectionView抛出实体框架附加异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆