संपत्ति डिफ़ॉल्ट मान के माध्यम से संबंध बदलने की कोशिश करते समय अप्रत्याशित InvalidOperationException


10

नीचे दिए गए नमूना कोड में मुझे निम्न अपवाद मिलते हैं db.Entry(a).Collection(x => x.S).IsModified = true:

System.InvalidOperationException: 'इकाई प्रकार' B 'का उदाहरण ट्रैक नहीं किया जा सकता क्योंकि कुंजी मान' {Id: 0} 'के साथ एक अन्य उदाहरण पहले से ही ट्रैक किया जा रहा है। मौजूदा संस्थाओं को संलग्न करते समय, सुनिश्चित करें कि किसी दिए गए मुख्य मूल्य के साथ केवल एक इकाई उदाहरण संलग्न है।

बी के उदाहरणों को संलग्न करने के बजाय इसे क्यों नहीं जोड़ा जाता है?

अजीब तरह से प्रलेखन संभव अपवाद के रूप में IsModifiedनिर्दिष्ट नहीं करता InvalidOperationExceptionहै। अमान्य दस्तावेज़ या बग?

मुझे पता है कि यह कोड अजीब है, लेकिन मैंने इसे केवल यह समझने के लिए लिखा है कि एफ़आई कोर कुछ अजीब मामलों में कैसे काम करता है। मैं जो चाहता हूं वह एक स्पष्टीकरण है, न कि एक काम।

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 और B कैसे संबंधित हैं? मतलब रिश्ते की संपत्ति क्या है?
सैम

जवाबों:


8

प्रदान किए गए कोड में त्रुटि का कारण इस प्रकार है।

जब आप Aडेटाबेस से निकाय बनाते हैं , तो इसकी संपत्ति Sएक संग्रह के साथ शुरू होती है जिसमें दो नए रिकॉर्ड होते हैं BIdइस नई Bसंस्थाओं में से प्रत्येक के बराबर है 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 {} };

इकाई के कोड var a = db.Set<A>().Single()कलेक्शन Sकी लाइन निष्पादित करने के बाद डेटाबेस से इकाइयां Aशामिल नहीं होती हैं B, क्योंकि DbContext Dbआलसी लोडिंग का उपयोग नहीं होता है और संग्रह का कोई स्पष्ट लोड नहीं होता है S। इकाई में Aकेवल नई Bइकाइयाँ होती हैं जिन्हें संग्रह के आरंभ के दौरान बनाया गया था S

जब आप IsModifed = trueसंग्रह Sइकाई ढांचे के लिए कॉल करते हैं तो उन दो नए एंटाइट Bको परिवर्तन ट्रैकिंग में जोड़ने की कोशिश करता है । लेकिन यह विफल रहता है क्योंकि दोनों नई Bसंस्थाओं में समान है 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;

आप स्टैक ट्रेस से देख सकते हैं कि एंटिटी फ्रेमवर्क Bसंस्थाओं में जोड़ने की कोशिश करता है 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)

और त्रुटि संदेश यह भी बताता है कि यह Bइकाई को ट्रैक नहीं कर सकता Id = 0क्योंकि Bउसी के साथ एक अन्य इकाई Idपहले से ही ट्रैक है।


इस समस्या को कैसे हल करें।

इस समस्या को हल करने के लिए आपको कोड को हटाना चाहिए जो संग्रह को Bप्रारंभ करते समय इकाइयाँ बनाता है S:

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

इसके बजाय आपको Sउस जगह पर संग्रह भरना चाहिए जहां Aबनाया गया है। उदाहरण के लिए:

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

यदि आप आलसी लोडिंग का उपयोग नहीं करते हैं, तो आपको Sअपने आइटम को परिवर्तन ट्रैकिंग में जोड़ने के लिए स्पष्ट रूप से संग्रह लोड करना चाहिए :

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

बी के उदाहरणों को संलग्न करने के बजाय इसे क्यों नहीं जोड़ा जाता है?

संक्षेप में , उन्हें जोड़ा जा रहा है, क्योंकि उनके पास Detachedराज्य है।

कोड की लाइन निष्पादित करने के बाद

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

इकाई के बनाए गए उदाहरणों में Bराज्य होता है Detached। इसे अगले कोड का उपयोग करके सत्यापित किया जा सकता है:

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

फिर जब आप सेट करें

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

ईएफ Bट्रैकिंग बदलने के लिए संस्थाओं को जोड़ने की कोशिश करता है । EFCore के स्रोत कोड से आप देख सकते हैं कि यह हमें तरीके से ले जाता है InternalEntityEntry.SetPropertyModified अगले तर्क मानों के साथ:

  • property- हमारी एक Bसंस्था,
  • changeState = true,
  • isModified = true,
  • isConceptualNull = false,
  • acceptChanges = true

इस तरह के तर्कों के साथ इस पद्धति में Detached Bप्रवेश की स्थिति बदल जाती है Modified, और फिर उनके लिए ट्रैकिंग शुरू करने की कोशिश की जाती है (देखें लाइनें 490 - 506)। क्योंकि Bसंस्थाओं के पास अब यह स्थिति Modifiedहै कि वे संलग्न (जोड़े नहीं) जाते हैं।


"बी के उदाहरणों को संलग्न करने के बजाय इसे क्यों नहीं जोड़ा जाता है?" आप कह रहे हैं "यह विफल रहता है क्योंकि दोनों नई बी इकाइयों में एक ही आईडी = 0 है"। मुझे लगता है कि यह गलत है क्योंकि एफई कोर 1 और 2 आईडी के साथ बचाता है। मुझे नहीं लगता कि यह इस प्रश्न के लिए सही उत्तर है
DIlshod K

@DIlshod K टिप्पणी के लिए धन्यवाद। "इस समस्या को कैसे हल करें" अनुभाग में मैंने लिखा है कि संग्रह Sको स्पष्ट रूप से लोड किया जाना चाहिए, क्योंकि बशर्ते कोड आलसी लोडिंग का उपयोग न करें। बेशक, EF ने Bडेटाबेस में पहले से बनाई गई संस्थाओं को बचाया । लेकिन कोड की लाइन A a = db.Set<A>().Single()केवल Aसंग्रह में संस्थाओं के बिना इकाई लोड करती है S। संग्रह को लोड करने के लिए Sउत्सुक लोडिंग का उपयोग किया जाना चाहिए। मैं अपने asnwer को स्पष्ट रूप से इस प्रश्न के उत्तर में शामिल करूँगा कि "B के उदाहरणों को संलग्न करने के बजाय इसे क्यों नहीं जोड़ा जाता है?"
इलियार तुरदुशेव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.