C
C#β€’2y ago
KooriByte

βœ… Collection appears to be null, even though it's not null on DB

I think the pictures already show the problem in more detail, there isn't much to it. User and Person have a many to many relationship, and that is all, I think... I don't have the slightest clue on why it says this. Would appreciate some help on this, tkss πŸ˜„
45 Replies
KooriByte
KooriByteOPβ€’2y ago
The issue here is that I can't test if this user is already related to this person originally it was just user.People.Add(person), but at some point I added those person lines too for some reason
djmurp
djmurpβ€’2y ago
yes, .Include(x => x.People) in the code inside GetUserAsync. This is so that EF only gets from the DB what you ask for (otherwise it would write very inefficient SQL even if you only wanted a single field)
KooriByte
KooriByteOPβ€’2y ago
oh, that is what he meant, I did not understand when he answered thank you a lot, will try it now I might be doing something wrong again is is suposed to be like this:
var user = _userManager.GetUserAsync(User.Include(x => x.People)).Result;
var user = _userManager.GetUserAsync(User.Include(x => x.People)).Result;
? forgot to correct ops in discord
djmurp
djmurpβ€’2y ago
do you own the method "GetUserAsync"? (can you edit it)
KooriByte
KooriByteOPβ€’2y ago
nop, it is from Identity this is the only User property that VS says is null the rest of them are showing just fine (name, email, etc)
djmurp
djmurpβ€’2y ago
unfortunately you can't .Include the navigation property called .People then. So you will have to get the user from the _context itself some properties are fine because they are columns in the same user table, but it won't go and get related entities from a different table (like .People)
KooriByte
KooriByteOPβ€’2y ago
I think I already done this, here
public async Task<IActionResult> Details(int? id)
{
if (id == null || _context.People == null)
{
return NotFound();
}

var person = await _context.People
.Include(x => x.ApplicationUsers)
.FirstOrDefaultAsync(m => m.Id == id);
if (person == null)
{
return NotFound();
}

return View(person);
}
public async Task<IActionResult> Details(int? id)
{
if (id == null || _context.People == null)
{
return NotFound();
}

var person = await _context.People
.Include(x => x.ApplicationUsers)
.FirstOrDefaultAsync(m => m.Id == id);
if (person == null)
{
return NotFound();
}

return View(person);
}
OOH i think I understood it know nah, I didn't :/
djmurp
djmurpβ€’2y ago
yep you've done the same thing, it's just that unfortunately you can't .Include that property when calling the built in Identity method - I thought you owned that code but you didn't do you know what table your users are in?
KooriByte
KooriByteOPβ€’2y ago
in my db?
djmurp
djmurpβ€’2y ago
yeh, or the equivalent property name in your context
KooriByte
KooriByteOPβ€’2y ago
users are in an ApplicationUser table, all "persons" are in a People table, and there is an intermediary table ApplicationUsersPeople between this two that have both the ApplicationUserId and PersonId for the many to many relationship I think that was the answer to your question, I'm not sure if I answered correctly, sorry :/
djmurp
djmurpβ€’2y ago
then you will have to query
var userWithPeople = _context.ApplicationUsers.Include(x => x.People).FirstOrDefault(x => x.Id == user.Id);
var userWithPeople = _context.ApplicationUsers.Include(x => x.People).FirstOrDefault(x => x.Id == user.Id);
or similar after the userManager.GetUserAsync(User) call
KooriByte
KooriByteOPβ€’2y ago
and then I change user in the code bellow for userWithPeople afterwards? will try it, just a minute
djmurp
djmurpβ€’2y ago
yes that should work in a readable way I have another code recommendation but we can solve this first
KooriByte
KooriByteOPβ€’2y ago
it said that the ApplicationUsers after _context was null I'll change it for _userManager, just a sec edit: this was just wrong
djmurp
djmurpβ€’2y ago
check this https://stackoverflow.com/questions/39837355/eager-loading-using-usermanager-with-ef-core, the answer for you actually in the question itself
KooriByte
KooriByteOPβ€’2y ago
oh, thank you, I'll read it do you know if there is another approach for this? I still couldn't make it work
djmurp
djmurpβ€’2y ago
another approach would be entity framework lazy loading (in that case accessing .People will trigger additional SQL) but i'd avoid if possible - it's convenient but much less efficient! you can investigate it to see if it suits your use case, but feel free to persist and post an update instead
jcotton42
jcotton42β€’2y ago
you really do not want to use lazy loading in EF surprise, synchronous, network IO on a property access is a nasty surprise
KooriByte
KooriByteOPβ€’2y ago
noooo, was half way into it already well, I think I'll try another solution this is way out of my current knowledge level, it's really frustrating sometimes
KooriByte
KooriByteOPβ€’2y ago
is there a reason why only People is not working here?
jcotton42
jcotton42β€’2y ago
oh, is this asp.net identity?
KooriByte
KooriByteOPβ€’2y ago
yep
jcotton42
jcotton42β€’2y ago
you might want to ask in #web or #database then
jcotton42
jcotton42β€’2y ago
Explicit Loading of Related Data - EF Core
Explicit loading of related data with Entity Framework Core
djmurp
djmurpβ€’2y ago
it's empty because you are going via userManager, something provided to you via Identity. It doesn't automatically know that you want all related data so it gives you the basics (column values). Anything more would result in a lot of SQL JOINs so the line below that line crashes?
jcotton42
jcotton42β€’2y ago
that's where explicit loading comes in it lets you load after the fact, without the foot bazooka of lazy loading
djmurp
djmurpβ€’2y ago
see above where I recommended the explicit loading
KooriByte
KooriByteOPβ€’2y ago
the line bellow was the other attempt to correct this
var userWithPeople = _context.ApplicationUsers.Include(x => x.People).FirstOrDefault(x => x.Id == user.Id) ;
var userWithPeople = _context.ApplicationUsers.Include(x => x.People).FirstOrDefault(x => x.Id == user.Id) ;
it is like this ApplicationUsers was null
djmurp
djmurpβ€’2y ago
you cannot explicitly load a navigation property via the user manager GetUserAsync(User) method, so you have to do it alone via the _context hmm
KooriByte
KooriByteOPβ€’2y ago
yes, this was in a link you sent previously. Problem was that I couldn't make it work too :c ApplicationUsers appeared null in this case
djmurp
djmurpβ€’2y ago
yeah ignore me. I am confusing @jcotton42 s responses by confusing eager loading with explicit loading, think ill go to sleep now.
jcotton42
jcotton42β€’2y ago
yeah the naming isn't very good classic microsoftism
djmurp
djmurpβ€’2y ago
so basically
var user = await _userManager.GetUserAsync(User);
_context.Entry(user) // lowercase user, NOT "User"!
.Collection(b => b.People)
.Load();
var user = await _userManager.GetUserAsync(User);
_context.Entry(user) // lowercase user, NOT "User"!
.Collection(b => b.People)
.Load();
KooriByte
KooriByteOPβ€’2y ago
do I need to put .Result after .GetUserAsync(User)?
jcotton42
jcotton42β€’2y ago
no that's what the await is for
jcotton42
jcotton42β€’2y ago
might want that I'd still ask in #database and/or #web I doubt you're the first to run into this
KooriByte
KooriByteOPβ€’2y ago
IT WORKED I used the .Result after, VS was screaming with me if I didn't use it
jcotton42
jcotton42β€’2y ago
no you want await foo not foo.Result using .Result makes it synchronous, and can result in deadlock
jcotton42
jcotton42β€’2y ago
Don't Block on Async Code
This is a problem that is brought up repeatedly on the forums and Stack Overflow. I think it’s the most-asked question by async newcomers once they’ve learned the basics.
djmurp
djmurpβ€’2y ago
you need to change your controller method (FavoritePerson) to return Task<IActionResult> and add the async modifier or follow the suggestions VS givees
KooriByte
KooriByteOPβ€’2y ago
if I take the Result off
_context.Entry(user)
.Collection(b => b.People)
.Load();
_context.Entry(user)
.Collection(b => b.People)
.Load();
stop working because b turns into a Task<ApplicationUser> I can put .Result after the user, but I think it makes it synchronous the same way
jcotton42
jcotton42β€’2y ago
@KooriByte that's why you await it
KooriByte
KooriByteOPβ€’2y ago
oh, I'm sorry, I forgot to add the await Well, I'll close this. You both saved my day, thank you so much for helping πŸ˜„ I'll read the links you sent to understand this better
Want results from more Discord servers?
Add your server