【问题标题】:The instance of entity type 'Article' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked无法跟踪实体类型“Article”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例
【发布时间】:2018-06-07 06:54:19
【问题描述】:

我正在为我的通用存储库编写单元测试,但是当我运行我的更新方法时它失败了。

我的测试方法如下:

    private async Task TestUpdate()
    {
        var compare = testArticles[0];
        var article = await testInstance.SelectSingleAsync(new AdHocSpecification<Article>(x => x.Id == compare.Id), x => x.ArticleImages).ConfigureAwait(false);

        Compare(article, compare);

        article.Brand = "Air-Bam";
        article.DescriptionDutch = "Ter aldus dus juist wij zware. Hadden met karank afzien dat oog dus invoer oorlog. Oogenblik zoo volledige zin mag stoompomp schatkist. Per had met tot sinds batoe zelfs. Dit opgericht producten ontrukten schatkist het. Verkoopen ons die omgewoeld gebergten honderden dus het.";
        article.DescriptionFrench = "Comme verts mes comme ces nul fut. Et ah te avons rente rouge je. Il ainsi il cause oh croix utile or. Jeunesse poitrine en epanouir la reparler la. Jet noble force par arret ras voila votre peu. Les ete appareil supplice vit epandent. Collines dissiper cavalier octogone la magasins ca.";
        article.Discount = 80;
        article.IsDeleted = true;
        article.Price = 1000;
        article.Title = "Air Tone";

        await testInstance.UpdateAsync(article).ConfigureAwait(false);

        Assert.AreNotEqual(article.Brand, compare.Brand);
        Assert.IsNotNull(article.ArticleImages);
        Assert.IsFalse(article.ArticleImages.ToList().SequenceEqual(compare.ArticleImages.ToList()));
        Assert.AreNotEqual(article.DescriptionDutch, compare.DescriptionDutch);
        Assert.AreNotEqual(article.DescriptionFrench, compare.DescriptionFrench);
        Assert.AreNotEqual(article.Discount, compare.Discount);
        Assert.AreNotEqual(article.IsDeleted, compare.IsDeleted);
        Assert.AreNotEqual(article.Price, compare.Price);
        Assert.AreNotEqual(article.Title, compare.Title);
    }

SelectSingleAsync 和 UpdateAsync 方法如下所示:

    public virtual Task<TObj> SelectSingleAsync(Specification<TObj> spec, params Expression<Func<TObj, object>>[] includes)
    {
        return _context.Set<TObj>().Includes(includes).Where(spec.ToExpression()).AsNoTracking().FirstOrDefaultAsync();
    }

    public virtual async Task UpdateAsync(TObj obj)
    {
        _context.Set<TObj>().Update(obj);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

当 Update 方法被执行时,抛出异常:

System.InvalidOperationException:无法跟踪实体类型“Article”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”来查看冲突的键值。

我不明白为什么它说它已经被跟踪了,因为我显然在我的选择方法中使用了AsNoTracking()。我能做些什么来解决这个问题?

编辑:Article 类如下所示

public class Article:Entity<Guid>
{
    public string Title { get; set; }
    public string Brand { get; set; }
    public string DescriptionDutch { get; set; }
    public string DescriptionFrench { get; set; }
    public decimal Price { get; set; }
    public int Discount { get; set; }

    public ICollection<ArticleImage> ArticleImages { get; set; }

    public override bool Equals(object obj)
    {
        if (!(obj is Article article)) return false;
        return article.Title.Equals(Title)
            && article.Id.Equals(Id)
            && article.IsDeleted.Equals(IsDeleted)
            && article.Brand.Equals(Brand)
            && article.DescriptionDutch.Equals(DescriptionDutch)
            && article.DescriptionFrench.Equals(DescriptionFrench)
            && article.Price.Equals(Price)
            && article.Discount.Equals(Discount)
            && article.ArticleImages.SequenceEqual(ArticleImages);
    }
}

public abstract class Entity<TKey> where TKey : struct
{
    [Key] public TKey Id { get; set; }

    public bool IsDeleted { get; set; }
}

我的上下文类如下所示:

public class ApplicationDbContext : IdentityDbContext<User>, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public ApplicationDbContext()
    {
    }

    public DbSet<HomePageItem> HomePageItem { get; set; }
    public DbSet<Country> Country { get; set; }
    public DbSet<Address> Address { get; set; }
    public DbSet<Translation> Translation { get; set; }
    public DbSet<Article> Article { get; set; }
    public DbSet<ArticleImage> ArticleImage { get; set; }
}

编辑 2:插入的文章示例:

        new Article
        {
            Brand = "Lorem",
            ArticleImages = new List<ArticleImage>{
                new ArticleImage
                {
                    Order = 0,
                    Url = "foo"
                },
                new ArticleImage
                {
                    Order = 1,
                    Url = "bar"
                }
            },
            DescriptionDutch = "Wier heft zien mont gaat zijn al en of. Wel brusch zin worden dienen bladen des vooral oosten. Nam behoeft noemden haalden elk. Stuit spijt enkel vogel oog een vindt geldt. Aangewend bezetting wijselijk arbeiders om is op antwerpen japansche af. Systemen planters vreemden kan hen passeert ons dichtbij dit. Met gevestigd wij financien als behoeften.",
            DescriptionFrench = "Air courtes reciter moi affreux croisee. La xv large en etais roidi ponts terre. Siens homme pic peu jeu glace beaux. Ca ma apres pitie sacre monde et voici. Battirent il echangent la croissent esplanade sortaient du ce. Fanatiques infanterie eux mon etonnement ecouterent imprudente assurances. Bambous fleurir ai arriere tu longues souffle etoffes un.",
            Discount = 10,
            IsDeleted = false,
            Price = 200,
            Title = "Tipfan"
        }

插入代码:

    public virtual async Task InsertAsync(TObj obj)
    {
        _context.Set<TObj>().Add(obj);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

【问题讨论】:

  • 你能发布定义Article实体的类吗?
  • 还有用于单元测试的上下文和实体的初始化?
  • 我已经更新了
  • 谢谢。但它缺少在单元测试中使用测试数据种子初始化您的inmemory 上下文。我想你做了这样的事情来“模拟”上下文。
  • 像这样初始化我的上下文:var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt; options.UseSqlite(connection)); 我在 TestInsert 方法中播种我的数据,在该方法中我成功添加了文章列表

标签: c# entity-framework-core


【解决方案1】:

尝试像这样修复您的代码:

     public class Article:Entity<Guid>
    {
        public string Title { get; set; }
        public string Brand { get; set; }
        public string DescriptionDutch { get; set; }
        public string DescriptionFrench { get; set; }
        public decimal Price { get; set; }
        public int Discount { get; set; }

        public ICollection<ArticleImage> ArticleImages { get; set; }

//add constructor, and initilize the id        
public Article(){
    this.Id = Guid.NewGuid();
    }

        public override bool Equals(object obj)
        {
            if (!(obj is Article article)) return false;
            return article.Title.Equals(Title)
                && article.Id.Equals(Id)
                && article.IsDeleted.Equals(IsDeleted)
                && article.Brand.Equals(Brand)
                && article.DescriptionDutch.Equals(DescriptionDutch)
                && article.DescriptionFrench.Equals(DescriptionFrench)
                && article.Price.Equals(Price)
                && article.Discount.Equals(Discount)
                && article.ArticleImages.SequenceEqual(ArticleImages);
        }
    }

【讨论】:

    【解决方案2】:

    当您从数据库中获取数据并对其进行迭代时,例如:

    var dataObject = await dbContext.Table.where(x=>x.UserId == model.UserId).TolistAsync();
    foreach(var item in dataObject)
    { 
    
    }
    

    不要创建另一个对象,将获取的对象直接传递给 Delete 或使用它来更新,因为 DbContext 正在跟踪它已获取的对象,而不是您创建的对象。例如:

    //错误代码

    var dataObject = await dbContext.Table.where(x=>x.UserId == model.UserId).TolistAsync();
    foreach(var item in dataObject)
    { 
       var x=new DataObject()
       {
          x=item.Id
       };
       dbContext.Table.Remove(x);
    }
    

    您必须将最初获取的实例传递给 Remove() 方法,请参阅:

    var dataObject = await dbContext.Table.where(x=>x.UserId == model.UserId).TolistAsync();
    foreach(var item in dataObject)
    { 
        dbContext.Table.Remove(item);
    }
    

    【讨论】:

      猜你喜欢
      • 2021-11-24
      • 2020-10-16
      • 2018-12-01
      • 2021-08-27
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 2023-01-03
      • 2020-01-31
      相关资源
      最近更新 更多