Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[MVC 5 / EF6] Navigational property is ineens NULL bij Edit

Pagina: 1
Acties:

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Ik zit te worstelen met MVC 5 in combinatie met Entity Framework 6.

Ik heb een database met Leagues en LeagueMembers (gebruikers die lid zijn van een League). Dit zijn de models:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    public class League : BaseModel
    {
        [Required]
        public string LeagueName { get; set; }

        public virtual ICollection<LeagueMember> Members { get; set; }
    }

    public class LeagueMember : BaseModel
    {
        [Required]
        public virtual ApplicationUser User { get; set; }
        [Required]
        public string UserId { get; set; }

        [Required]
        public virtual League League { get; set; }
        [Required]
        public int LeagueId { get; set; }

        [Required]
        public bool IsOwner { get; set; }

        [Required]
        public int AdminLevel { get; set; }
    }


Er kunnen meerdere gebruikers zijn die allemaal hun eigen Leagues aanmaken (en daar Owner van zijn).

Bij het wijzigen van een League (met een Edit action) moet er gevalideerd worden dat de huidige gebruiker genoeg rechten heeft om die League te wijzigen. Dit gebeurt twee keer: (1) eerst in de GET request naar de Edit pagina, en daarna nog eens in de POST request van de Edit pagina.


Het probleem is dat de check in (1) goed gaat, en in (2) niet, omdat de 'Members' navigational property dan ineens null is geworden. In de GET request is de Members property niet null en bevat hij de juiste members, maar zodra ik op de Edit knop klik op de edit pagina (en dus naar de POST request spring) is hij null en gaat de validatie dus elke keer mis (er zijn geen members dus de huidige gebruiker kan nooit genoeg rechten hebben).


Dit zijn de twee Edit methods in de controller. Ik heb in beide een Debug.Assert toegevoegd die controlleert of de Members property null is. In de eerste method (GET) is dat niet zo (klopt dus), in de tweede is hij wel null...

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        // GET: /League/Edit/5
        [Authorize]
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            League league = Database.Leagues.Find(id);
            if (league == null)
            {
                return HttpNotFound();
            }

            // TEST: ------------------------------
            Debug.Assert(league.Members != null);                
            // Resultaat: league.Members is hier NIET null - ok!
            // ------------------------------------

            // Authenticate - check if user is league admin
            var user = UserManager.FindById(User.Identity.GetUserId());
            if (!user.IsLeagueAdmin(league, AdminLevels.EditLeague))
            {
                return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
            }

            return View(league);
        }

        // POST: /League/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        public ActionResult Edit([Bind(Include="Id,LeagueName,Description,Website,AccessType,Password,CreatedTime,UpdatedTime")] League league)
        {
            if (ModelState.IsValid)
            {
                // TEST: ------------------------------
                Debug.Assert(league.Members != null);
                // Resultaat: league.Members is hier NULL! - Fout!
                // ------------------------------------

                // Authenticate - check if user is league admin
                var user = UserManager.FindById(User.Identity.GetUserId());
                if (!user.IsLeagueAdmin(league, AdminLevels.EditLeague))
                {
                    return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
                }

                Database.Entry(league).State = EntityState.Modified;
                Database.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(league);
        }


Ik snap niet waar dit door komt. Heeft het met lazy loading te maken? En zo ja, waarom gaat het dan wel goed in de GET maar niet in de POST?

Mijn iRacing profiel


Verwijderd

In de GET-method doe je eerst een Find op de database waardoor die relatie van Members wordt geladen en daarna test je of deze niet null is. Maar in de POST-method doe je het andersom. Dan is het wel logisch dat ie null is. Tenzij je hem in je form opneemt en meegeeft in de model-binding, maar dat is nu het geval. Dus als je die findbyid() en assert() omdraait in je POST-action zou hij wel gevuld moeten zijn met members...

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
In de POST method krijg ik de League al als argument mee gestuurd, ik haal hem dus niet zelf uit de database.

De FindById doe ik op de UserManager om de huidige user te vinden, dat heeft niks met de League te maken.

Moet ik handmatig de members op gaan halen mbv het League ID? Dit zou toch wel automatisch door EF moeten kunnen?

Mijn iRacing profiel


  • Phyxion
  • Registratie: April 2004
  • Niet online

Phyxion

_/-\o_

Ik neem aan dat Members niet meegestuurd wordt met de post? Dan is het logisch namelijk. Bestaat het id al wel van de league bij de post method? Zo ja, gebruik de attach method op de entity, dan zal je members wel gevuld zijn. Alhoewel ik niet helemaal begrijp wat je uberhaupt aan het doen bent in de post method. Je roept SaveChanges aan maar doet vervolgens helemaal niks met de entity?

'You like a gay cowboy and you look like a gay terrorist.' - James May


  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
De entity is al veranderd "league" heeft namelijk de waarden die ik in het edit form invul. Nu ik er over nadenk is het ook logisch inderdaad dat Members null is omdat die niet in de post meekomt, maar hoe kan ik er dan voor zorgen dat EF die toch even (liefst automatisch) laadt? Want het ID is inderdaad al bekend.

Dit werkt niet:
C#:
1
Database.Leagues.Attach(league);


Dit werkt wel, maar is wel erg 'handmatig':
C#:
1
league.Members = Database.LeagueMembers.Where(m => m.LeagueId == league.Id).ToList();

Mijn iRacing profiel


Verwijderd

NickThissen schreef op zondag 06 april 2014 @ 17:13:
Dit werkt wel, maar is wel erg 'handmatig':
C#:
1
league.Members = Database.LeagueMembers.Where(m => m.LeagueId == league.Id).ToList();
Voer die query uit in de constructor van je model League. Komt hij altijd mee met het model.

C#:
1
2
3
public League(){
this.Members = Database.LeagueMembers.Where(m => m.LeagueId == league.Id).ToList();
}

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Maar 9 van de 10 keer is het niet nodig en wordt hij automatisch al geladen, dan ga ik dus dubbelop laden?

Daarnaast heb ik in de constructor geen toegang tot het Database object natuurlijk.

[ Voor 29% gewijzigd door NickThissen op 06-04-2014 17:20 ]

Mijn iRacing profiel


  • Haan
  • Registratie: Februari 2004
  • Laatst online: 15:31

Haan

dotnetter

Wat is het probleem dat league.Members null is in de Edit post method? Want je doet er toch niets mee? Zoals gezegd is het logisch dat deze property leeg is, er wordt namelijk een League object gecreëerd met de waardes de in de POST worden meegegeven en daar zitten geen members in. Als je die toch nodig hebt, zal je het object weer even op moeten vragen.

Kater? Eerst water, de rest komt later


  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Ik heb ze nodig om te kunnen kijken of de gebruiker wel lid is van de league en genoeg admin rechten heeft.
In de IsLeagueAdmin method.

Het werkt op deze manier inderdaad, maar het lijkt aardig 'handmatig' terwijl ik had verwacht dat EF dit wel voor me zou kunnen afhandelen (met die Attach bijv, maar dat lijkt niks te doen)?

[ Voor 51% gewijzigd door NickThissen op 06-04-2014 21:53 ]

Mijn iRacing profiel


  • Haan
  • Registratie: Februari 2004
  • Laatst online: 15:31

Haan

dotnetter

Zelfs als Attach had gewerkt, had je toch zelf een method aangeroepen, dus ik zie niet hoe dat minder 'handmatig' is dan de Find method gebruiken ;)

Kater? Eerst water, de rest komt later


  • GotG
  • Registratie: April 2008
  • Laatst online: 11:53
je gaat sowieso iets handmatigs moeten doen

wat je ook kan doen is deze lijn wat vroeger zetten
C#:
1
Database.Entry(league).State = EntityState.Modified; 

en dan manueel de "related entities inladen door middel van:
C#:
1
Database.Entry(league).Collection(l => l.Members).Load();

dit zou er voor moeten zorgen dat de members ingeladen worden
waarna je dan je checks zou kunnen doen.

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 09:34

Sebazzz

3dp

Of je doet iets in deze trant (pseudocode):
• Get from database
• TryUpdateModel(entity)

Klaar :)

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]

Pagina: 1