Özellik varsayılan değeri üzerinden ilişki değiştirilmeye çalışılırken beklenmeyen InvalidOperationException


10

Aşağıdaki örnek kod yaparken ben aşağıdaki istisna olsun db.Entry(a).Collection(x => x.S).IsModified = true:

System.InvalidOperationException: '' {Id: 0} 'anahtar değerine sahip başka bir örnek zaten izlendiği için' B 'varlık türü örneği izlenemiyor. Mevcut varlıkları eklerken, verilen anahtar değerine sahip yalnızca bir varlık örneğinin eklendiğinden emin olun.

B örneklerini eklemek yerine neden eklemiyor?

Garip belgeleri IsModifiedbelirtmez InvalidOperationExceptionolası bir istisna olarak. Geçersiz dokümantasyon mu yoksa hata mı?

Bu kod garip olduğunu biliyorum, ama sadece ef çekirdek bazı garip egde durumlarda nasıl çalıştığını anlamak için yazdım. İstediğim bir açıklama, bir çözüm değil.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    public class A
    {
        public int Id { get; set; }
        public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
    }

    public class B
    {
        public int Id { get; set; }
    }

    public class Db : DbContext {
        private const string connectionString = @"Server=(localdb)\mssqllocaldb;Database=Apa;Trusted_Connection=True";

        protected override void OnConfiguring(DbContextOptionsBuilder o)
        {
            o.UseSqlServer(connectionString);
            o.EnableSensitiveDataLogging();
        }

        protected override void OnModelCreating(ModelBuilder m)
        {
            m.Entity<A>();
            m.Entity<B>();
        }
    }

    static void Main(string[] args)
    {
        using (var db = new Db()) {
            db.Database.EnsureDeleted();
            db.Database.EnsureCreated();

            db.Add(new A { });
            db.SaveChanges();
        }

        using (var db = new Db()) {
            var a = db.Set<A>().Single();
            db.Entry(a).Collection(x => x.S).IsModified = true;
            db.SaveChanges();
        }
    }
}

A ve B arasındaki ilişki nedir? anlamı ilişki özelliği nedir?
sam

Yanıtlar:


8

Sağlanan koddaki hatanın nedeni aşağıdadır.

AVeritabanından oluşturulmuş varlık aldığınızda, özelliği Siki yeni kayıt içeren bir koleksiyonla başlatılır B. Idbu yeni Bvarlıkların her biri eşittir 0.

// This line of code reads entity from the database
// and creates new instance of object A from it.
var a = db.Set<A>().Single();

// When new entity A is created its field S initialized
// by a collection that contains two new instances of entity B.
// Property Id of each of these two B entities is equal to 0.
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };

Kod satırını çalıştırıldıktan sonra var a = db.Set<A>().Single()koleksiyonun Svarlığın Aiçermediğinden Bdolayı, veritabanından varlıkları DbContext Dbtembel yükleme kullanmak ve tahsilat hiçbir açık yükleme var demektir S. Varlık Ayalnızca Bkoleksiyonun başlatılması sırasında oluşturulan yeni varlıkları içerir S.

IsModifed = trueKoleksiyon Svarlık çağrısı yaparken, bu iki yeni girişi Bdeğişiklik izlemeye eklemeye çalışır . Ancak başarısız olur, çünkü her iki yeni Bvarlık da aynıdır Id = 0:

// This line tries to add to change tracking two new B entities with the same Id = 0.
// As a result it fails.
db.Entry(a).Collection(x => x.S).IsModified = true;

Sen varlık çerçeve çalışır eklemek için bu yığın izleme görebilirsiniz Biçine varlıkları IdentityMap:

at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified, Boolean isConceptualNull, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(InternalEntityEntry internalEntityEntry, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(Object relatedEntity, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.set_IsModified(Boolean value)

Ve hata iletisi de izleyemez söyler Bile varlık Id = 0başka nedeni Bile aynı varlık Idzaten izlenir.


Bu sorun nasıl giderilir.

Bu sorunu gidermek için koleksiyonu Bbaşlatırken varlıklar oluşturan kodu silmeniz gerekir S:

public ICollection<B> S { get; set; } = new List<B>();

Bunun yerine Skoleksiyonu Aoluşturulduğu yere doldurmalısınız . Örneğin:

db.Add(new A {S = {new B(), new B()}});

Tembel yükleme kullanmıyorsanız S, öğelerini değişiklik izlemeye eklemek için koleksiyonu açıkça yüklemelisiniz :

// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;

B örneklerini eklemek yerine neden eklemiyor?

Kısacası , eklenmeye meyillidirler çünküDetached devlete .

Kod satırını yürüttükten sonra

var a = db.Set<A>().Single();

oluşturulan varlık örnekleri Bdevlete sahiptir Detached. Sonraki kod kullanılarak doğrulanabilir:

Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);

Sonra,

db.Entry(a).Collection(x => x.S).IsModified = true;

EF, Bizlemeyi değiştirmek için varlık eklemeye çalışır . EFC'nin kaynak kodundanDaha fazla bunun bizi bir sonraki argüman değerleriyle InternalEntityEntry.SetPropertyModified yöntemine götürdüğünü görebilirsiniz :

  • property- Bvarlıklarımızdan biri ,
  • changeState = true,
  • isModified = true,
  • isConceptualNull = false,
  • acceptChanges = true.

Bu tür argümanlarla bu yöntem, Detached Bgirişlerin durumunu değiştirir Modifiedve daha sonra onlar için izlemeye başlamaya çalışır (bkz . 490 - 506 satırları ). Çünkü Btarafların artık devlet var Modifiedonları takılacak bu yol (eklenmez).


"B örneklerini eklemek yerine neden eklemiyor?" "Başarısız oluyor çünkü her iki yeni B varlığı da aynı Id = 0 değerine sahip" diyorsunuz. Yanlış olduğunu düşünüyorum çünkü ef çekirdek hem 1 hem de 2 id ile kaydeder. Bu soru için doğru cevap olduğunu sanmıyorum
DIlshod K

@DIlshod K Yorum için teşekkürler. "Bu sorun nasıl giderilir" bölümünde S, sağlanan kodun tembel yükleme kullanmadığı için koleksiyonun açıkça yüklenmesi gerektiğini yazdım . Elbette EF daha önce oluşturulmuş Bvarlıkları veritabanına kaydetti. Ancak kod satırı A a = db.Set<A>().Single()yalnızca Akoleksiyonda varlık olmayan varlıkları yükler S. Koleksiyon yüklemek için Sistekli yükleme kullanılmalıdır. Asnwer'ımı "B örneklerini eklemek yerine neden eklemiyor?" Sorusuna açıkça yanıt verecek şekilde değiştireceğim.
Iliar Turdushev
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.