Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
EF Core Fehler beim Erzeugen neuer Datensätze
dscNRW
myCSharp.de - Member



Dabei seit:
Beiträge: 21

Themenstarter:

EF Core Fehler beim Erzeugen neuer Datensätze

beantworten | zitieren | melden

Verwendetes Datenbanksystem: MSSQL Developer Edition

Hallo zusammen,

ich habe seit ein paar Tagen ein Problem wo ich trotz Recherche nicht weiterkomme.

Ich habe ein EF Modell und die entsprechenden Klassen auf Basis einer SQL-Datenbank aufgebaut.
Wenn ich nun einen neuen Datensatz hinzufügen möchte erhalte ich folgenden Fehler:

Fehler
System.InvalidOperationException: "The value of 'Documents.ID' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known."

Ich kann nicht wirklich ausmachen welche Tabelle das Problem verursacht - vermute aber die Tabelle Matchcodes.....

Die Klassen zu den beiden Entitäten "Matchcodes" und "Documents" sehen wie folgt aus:

Matchcodes


    public class Matchcodes
    {
        [Key]
        public int ID { get; set; }

        public string Matchcode { get; set; }

        public int DocumentsID { get; set; }

        public virtual Documents Documents { get; set; }
    }


Documents


    public class Documents
    {
        private ILazyLoader LazyLoader { get; set; }

        public  Documents() 
        {
            _matchcodes = new HashSet<Matchcodes>();
            _documentNotices = new HashSet<DocumentNotices>(); 
        }
        private Documents(ILazyLoader lazyloader)
        {
            LazyLoader = lazyloader;
        }
        [Key]
        public int ID { get; set; }

        public string DocumentName { get; set; }

        public int DocumentType { get; set; }

        public decimal DocumentSize { get; set; }

        public byte[] DocumentBinary { get; set; }

        public string DocumentPath { get; set; }

        public string DocumentOrigin { get; set; }

        public int FolderID { get; set; }

        public int Revision { get; set; }

        public DateTimeOffset? CreationDate { get; set; }

        public DateTime? DocumentDate { get; set; }
        public int OriginalDocument { get; set; }

        public bool ForAll { get; set; }

        public int UserID { get; set; }

        [NotMapped]

        public bool Concat { get; set; }
        [NotMapped]

        public bool DoOCR { get; set; }
        public FileTypes FileType { get; set; }

        public DocumentsFullText DocumentsFullText { get; set; }

        private ICollection<Matchcodes> _matchcodes;
        public ICollection<Matchcodes> Matchcodes
        {
            get => LazyLoader.Load(this, ref _matchcodes);
            set => _matchcodes = value;
        }

        private ICollection<DocumentNotices> _documentNotices;

        public ICollection<DocumentNotices> DocumentNotices
        {
            get => LazyLoader.Load(this, ref _documentNotices);
            set => _documentNotices = value;
        }

        [NotMapped]
        public string FileTypeAsString
        {
            get
            {
                return App.ModelContext.FileTypes.Where(x => x.ID == DocumentType).FirstOrDefault().FileType;
            }
        }
    }

Im Kontext selbst habe ich via Fluent API folgendes gesetzt:


    protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Users>()
                .HasOne(u => u.ProgramUsage)
                .WithOne()
                .HasForeignKey<Users>(x => x.ID);

            modelBuilder.Entity<Documents>()
                .HasOne(d => d.FileType)
                .WithOne()
                .HasForeignKey<Documents>(x => x.ID);

            modelBuilder.Entity<Documents>()
             .HasMany<Matchcodes>(x => x.Matchcodes)
             .WithOne()
             .HasForeignKey(x=>x.ID);

            modelBuilder.Entity<Documents>()
             .HasOne(d => d.DocumentsFullText)
             .WithOne()
             .HasForeignKey<Documents>(x => x.ID);
        }
    }


Ich habe schon ein paar Änderungen im Kontext durchgeführt und ein paar Dokus dazu gelesen aber ich vermute mal, dass ich irgendetwas grandios Missverstanden habe.

Habt ihr einen Denkanstoß?

Gruß,
D.

P.S.:
Falls Abt das lesen sollte - DateTime? sollte nicht verwendet werden - ist mir dank dir bekannt und wird auch noch geändert - aber zunächst möchte ich das andere Problem lösen :-)
Attachments
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4.494

beantworten | zitieren | melden

Kann es sein, daß du Primary Key (PK) und Foreign Key (FK) (für Documents) durcheinandergebracht hast, s.a. Beispiele in The Fluent API HasKey Method und The Fluent API HasForeignKey Method?

PS: Die Entitäten-Klassen sollten besser in der Einzahl benannt sein, also User, MatchCode, Document.
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von Th69 am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16.196

beantworten | zitieren | melden

Wichtiger Punk bei EF Core: Mische keine Annotations mit der Fluent API.
Das ist fürchterlich für die Übersicht und kann sich schnell inhaltlich widersprechen, dann is die Bugsuche groß.
Recommendation ist: Fluent API.

Das Konstrukt sieht nach einem bösen Workaround aus, wo Du aus einer Entiätsinstanz auf eine globale Klasse zugreifen willst.


return App.ModelContext.FileTypes.Where(x => x.ID == DocumentType).FirstOrDefault().FileType;
Warum ist das da drin? Eine Entität sollte eine "dumme" Datenklasse sein und nichts ausführen.
Im schlimmsten Fall kann das zu Race Condition und Locks führen.

Ich sehe auch eine Verletzung in den Keys, aber kann auch an einem generell falschen Umgang mit Entitäten liegen, den wir in dem Codestück aber nicht sehen.

Ansonsten noch die Hinweise:
Arbeiten mit Nullable-Verweistypen – EF Core
Lazy Loading zugehöriger Daten – EF Core
Implementieren der Infrastrukturpersistenzebene mit Entity Framework Core
private Nachricht | Beiträge des Benutzers
dscNRW
myCSharp.de - Member



Dabei seit:
Beiträge: 21

Themenstarter:

beantworten | zitieren | melden

Zitat
PS: Die Entitäten-Klassen sollten besser in der Einzahl benannt sein, also User, MatchCode, Document.

Danke dir TH69 - werde ich noch korrigieren. Die Links lese ich mir gerade durch.

Zitat
Wichtigster Punk bei EF Core: Mische keine Annotations mit der Fluent API.
Das ist fürchterlich für die Übersicht. Recommendation ist: Fluent API.

Habe die Annotations raus genommen und "versuche" das nun über die Flusend API zu realisieren.

Zitat
Das Konstrukt sieht nach einem bösen Workaround aus, wo Du aus einer Entiätsinstanz auf eine globale Klasse zugreifen willst.

return App.ModelContext.FileTypes.Where(x => x.ID == DocumentType).FirstOrDefault().FileType;
Copy
Warum ist das da drin? Eine Entität sollte eine "dumme" Datenklasse sein und nichts ausführen.

Habe ich rausgenommen - war auch nicht in Verwendung und sollte "nur" zum testen dienen.

Ich habe die IDs innerhalb der Entitäten nun vorläufig umbenannt - ich möchte das aber auch noch nach TH69's Vorschlag in den Singular ändern.
Würde aber vorher gerne das Problem verstehen und lösen damit ich damit nicht am laufenden Band auf die Nase falle.

In wie weit meinst du Verletzung der Keys? Könntest du mir da einen kleinen Tipp geben?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16.196

beantworten | zitieren | melden

Erstmal technisch: Du arbeitest mit integer Keys, die i.d.R. durch die Datenbank erzeugt werden, nicht in der Applikation.
Das bedeutet, dass die "Id"-Eigenschaften leer (0) sind, wenn Du ein neues Objekt erzeugst. Die Id wird erst mit dem Speichern und der Antwort der Datenbank gesetzt.

Du kannst also keine Ids verwenden, bis Du die Entität gespeichert hast.
Irgendwo in Deinem Quellcode scheinst Du aber bereits mit den Werten der Id zu arbeiten (zB. Zuweisung auf einen FK in ein anderes Objekt), obwohl der Wert Id noch gar nicht feststeht.
Meistens, meine Erfahrung, liegt das daran, weil man Applikationslogik mit Datenbanklogik zu sehr vermischt.
Zitat
Würde aber vorher gerne das Problem verstehen und lösen damit ich damit nicht am laufenden Band auf die Nase falle.
Basics wie ein Naming kannst Du trotzdem immer anpassen.
Macht es allen Beteiligten dann auch leichter.
private Nachricht | Beiträge des Benutzers
dscNRW
myCSharp.de - Member



Dabei seit:
Beiträge: 21

Themenstarter:

beantworten | zitieren | melden

Hallo Abt,
hallo TH69,

danke für die Info :-).

Ich werde das Projekt nochmal neu anfangen - war Gott sei Dank noch nicht so weit.

Werde mir dabei eure Links und Ratschläge zu Herzen nehmen.
private Nachricht | Beiträge des Benutzers