[EF] Entities zijn "modified" zonder wijzigingen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 00:51

F.West98

Alweer 16 jaar hier

Topicstarter
Dag :w

Ik heb op dit moment een gek probleem met het volgende stukje code:
C#:
1
2
3
4
5
6
7
var foos = await this.AppContext.Foos
                    .Where(s => s.SomeEnum == this.SomeEnum)
                    .OfType<T>()
                    .Include(s => s.ListOfThings)
                    .Include(s => s.ListOfThings.Select(a => a.ItemOfThing)
                    .Include(s => s.SingleItem)
                    .ToListAsync();

Na het ophalen van deze entities staan alle Things in ListOfThings gemarkeerd als "Modified" in de state manager. Zonder dat ik er iets aan wijzig dus. Dat is al gek.
Het probleem is dat ik bij het opslaan daarna een welbekende fout krijg:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
Deze fout kreeg ik eerst na wijzigingen, maar na wat testen dus ook zonder enige wijziging gemaakt te hebben!
Als ik voor elke Thing de state in een poging de entities te negeren op Unchanged zet gaat dat ook fout, met dezelfde melding :?

Ik snap er kort samengevat dus helemaal niets meer van...

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


Acties:
  • 0 Henk 'm!

  • Jogai
  • Registratie: Juni 2004
  • Laatst online: 00:08
Kun je in de innerexception niet zien op welke relatie hij faalt? Misschien verhelderd dat iets.

Klik hier om op linkedIn lid te worden van de Freelance Tweakers groep.


Acties:
  • 0 Henk 'm!

  • BM
  • Registratie: September 2001
  • Laatst online: 20:37

BM

Moderator Spielerij
Heb niet al te veel ervaring met EF, maar als ik MSDN er op na sla kom ik deze zin tegen:
To include a collection and then a reference one level down:.
C#:
1
query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference))

Maakt regel 5 in jouw voorbeeld daarmee regel 4 niet overbodig? En wat gebeurd er als je regel 4 achterwege laat?

[ Voor 4% gewijzigd door BM op 08-07-2015 11:02 ]

Xbox
Even the dark has a silver lining | I'm all you can imagine times infinity, times three


Acties:
  • 0 Henk 'm!

  • InZane
  • Registratie: Oktober 2000
  • Laatst online: 17-09 13:15
Een EntityState.Modified bij een select query. Dat is ook nieuw voor mij.
Ik zou inderdaad eerst proberen uit te zoeken op welke relatie hij faalt. Als de debugger geen nuttige info geeft, zou je kunnen proberen om de includes één voor één te verwijderen totdat er geen Exception meer wordt gegooid.
BM schreef op woensdag 08 juli 2015 @ 11:02:
Heb niet al te veel ervaring met EF, maar als ik MSDN er op na sla kom ik deze zin tegen:

[...]

C#:
1
query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference))

Maakt regel 5 in jouw voorbeeld daarmee regel 4 niet overbodig? En wat gebeurd er als je regel 4 achterwege laat?
Lijkt dubbelop inderdaad.

[ Voor 42% gewijzigd door InZane op 08-07-2015 11:05 ]


Acties:
  • 0 Henk 'm!

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 00:51

F.West98

Alweer 16 jaar hier

Topicstarter
Jogai schreef op woensdag 08 juli 2015 @ 10:56:
Kun je in de innerexception niet zien op welke relatie hij faalt? Misschien verhelderd dat iets.
InnerException is null....
InZane schreef op woensdag 08 juli 2015 @ 11:02:
Een EntityState.Modified bij een selecte query. Dat is ook nieuw voor mij.
Ik zou inderdaad eerst proberen uit te zoeken op welke relatie hij faalt. Als de debugger geen nuttige info geeft, zou je kunnen proberen om de includes één voor één te verwijderen totdat er geen Exception meer wordt gegooid.


[...]


Lijkt dubbelop inderdaad.
Als ik de Include weghaal laadt hij die entities niet en werkt het verder prima (alleen kickt even later de lazy loading in waardoor ze alsnog (verkeerd) geladen worden met hetzelfde gedoe).
Het ligt dus duidelijk aan die Thing-entities. Deze fungeren als meer-op-meer koppeling tussen twee entities (Foo en Bar). Aangezien er meer opties zijn bij die relatie is een simpele koppeltabel door EF niet voldoende. Het lijkt er dus op dat er iets misgaat dat de Things niet goed zijn gekoppeld aan de Bars. Ofzo.

[ Voor 15% gewijzigd door F.West98 op 08-07-2015 11:30 ]

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


Acties:
  • 0 Henk 'm!

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 00:51

F.West98

Alweer 16 jaar hier

Topicstarter
Ooooh hé. Gevonden :*)

De Foos hebben meerdere entities die Foo inheriten in dezelfde tabel (EF maakt dan een discriminator). Deze classes geven enkel context aan de applicatie en hebben verder geen eigen velden.
Dus MainFoo, ResultFoo, SideFoo.
Zo heb ik ook 3 overloads van de koppelentity (backwards-compatibility met de oude code), MainThing, ResultThing en SideThing.
En toen heb ik, voor het gemak, in MainThing de link naar de Foo overschreven:
C#:
1
2
3
4
5
6
7
public class Thing { 
    public virtual Foo Foo { get; set; }
}

public class MainThing : Thing {
    public new virtual MainFoo Foo { get; set; }
}

En daar ging het dus mis. EF herkende niet dat dat dezelfde was (ondanks attribuut dat het naar dezelfde FK-kolom verwijst), en daardoor ging het mis.
Maar hoe krijg ik nu wel het gemak daarvan zonder de problemen? Dus dat MainThing.Foo een MainFoo geeft en Thing.Foo gewoon een Foo? (en zo ook ResultThing.Foo een ResultFoo en SideThing.Foo een SideFoo)

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:53

Haan

dotnetter

Mijn ervaring met EF en inheritance op entity classes is dat EF er vaak helemaal niks van bakt (en al helemaal niet als je met Code First werkt en een wijziging aanbrengt :X )

Ik snap je huidige constructie niet helemaal, maar als je er vrij zeker van bent dat je toch verder niks doet met die afgeleide classes, zou ik in jouw geval misschien een enum toevoegen dat het soort Foo beschrijft:
C#:
1
2
3
4
5
6
7
public enum FooType { Main, Result, Side}

public class Thing 
{ 
    public virtual Foo Foo { get; set; }
    public FooType Type { get; set; }
}

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • kutagh
  • Registratie: Augustus 2009
  • Laatst online: 21:37
Is het uberhaupt nodig dat de return type MainFoo moet zijn? Als MainFoo inherited van Foo en binnen MainThing je een MainFoo aanmaakt en deze teruggeeft als Foo, blijft de type nog steeds MainFoo. Door dan oftewel de instance methods te overriden ofwel gebruik maken van overloaded methods kan je dan custom behavior voor je MainFoo / ResultFoo / SideFoo definieren.

Acties:
  • 0 Henk 'm!

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 00:51

F.West98

Alweer 16 jaar hier

Topicstarter
Haan schreef op woensdag 08 juli 2015 @ 12:31:
Mijn ervaring met EF en inheritance op entity classes is dat EF er vaak helemaal niks van bakt (en al helemaal niet als je met Code First werkt en een wijziging aanbrengt :X )

Ik snap je huidige constructie niet helemaal, maar als je er vrij zeker van bent dat je toch verder niks doet met die afgeleide classes, zou ik in jouw geval misschien een enum toevoegen dat het soort Foo beschrijft:
C#:
1
2
3
4
5
6
7
public enum FooType { Main, Result, Side}

public class Thing 
{ 
    public virtual Foo Foo { get; set; }
    public FooType Type { get; set; }
}
Het probleem is dan dat je overal die enum moet gaan meegeven en ik ook een boel legacy-code moet gaan veranderen. De oude opzet was namelijk voor elk van de 3 soorten een kopie van het model, die heb ik nu samengevoegd. Daarom heb ik voor deze oplossing gekozen.
kutagh schreef op woensdag 08 juli 2015 @ 12:33:
Is het uberhaupt nodig dat de return type MainFoo moet zijn? Als MainFoo inherited van Foo en binnen MainThing je een MainFoo aanmaakt en deze teruggeeft als Foo, blijft de type nog steeds MainFoo. Door dan oftewel de instance methods te overriden ofwel gebruik maken van overloaded methods kan je dan custom behavior voor je MainFoo / ResultFoo / SideFoo definieren.
Op dit moment is een groot deel van de code als volgt:
C#:
1
MainFoo mainFoo = mainThing.Foo; // geen var

Waardoor ik overal casts moet gaan zitten toevoegen, en dus heel veel code moet gaan veranderen. Ik vroeg me af of het niet beter kan door het zo te doen dat MainThings altijd MainFoos doen en gewone Things altijd gewone Foos enzovoorts.

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:53

Haan

dotnetter

Algemene opmerking: een consequentie van code refactoren is vaak dat je een hoop bestaande code aan moet passen. Ik zou niet de keus voor een bepaalde implementatie af van laten hangen van de hoeveelheid werk om bestaande code te herschrijven, maar gewoon de beste oplossing kiezen ;)
(uitzonderingen daargelaten natuurlijk, bijvoorbeeld als het verschil in werk gigantisch groot is en/of een bepaalde keuze niet significant beter is)

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 00:51

F.West98

Alweer 16 jaar hier

Topicstarter
Haan schreef op woensdag 08 juli 2015 @ 15:13:
Algemene opmerking: een consequentie van code refactoren is vaak dat je een hoop bestaande code aan moet passen. Ik zou niet de keus voor een bepaalde implementatie af van laten hangen van de hoeveelheid werk om bestaande code te herschrijven, maar gewoon de beste oplossing kiezen ;)
(uitzonderingen daargelaten natuurlijk, bijvoorbeeld als het verschil in werk gigantisch groot is en/of een bepaalde keuze niet significant beter is)
Daarnaast vind ik deze optie duidelijker en overzichtelijker dan overal een parameter toevoegen. Het juiste type meegeven met een paar classes met generics vind ik toch wat netter imo.

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI

Pagina: 1