C
C#16mo ago
S-IERRA

❔ EFC Is not loading collection properly

I have the following 2 models in my database, EFC does not seem to be loading it at all for some reason
public record ChatUser : IEntityTypeConfiguration<ChatUser>
{
public Guid Id { get; set; }

public required string Username { get; set; }
public required string Email { get; set; }
public required string HashedPassword { get; set; }

public Guid? RegistrationToken { get; set; }
public Guid? PasswordResetToken { get; set; }

public virtual ICollection<ChatChannel> Channels { get; set; } = new HashSet<ChatChannel>();
public virtual ICollection<ChatRelationship> Relationships { get; set; } = new HashSet<ChatRelationship>();

public void Configure(EntityTypeBuilder<ChatUser> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());
}
}


public record ChatRelationship : IEntityTypeConfiguration<ChatRelationship>
{
public Guid Id { get; set; }

public required Guid SelfId { get; set; }
public ChatUser Self { get; set; }

public required Guid OtherUserId { get; set; }
public ChatUser OtherUser { get; set; }

public required ChatRelationShipType Type { get; set; } = ChatRelationShipType.None;

public void Configure(EntityTypeBuilder<ChatRelationship> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());

builder.HasKey(f => new { User1Id = f.SelfId, User2Id = f.OtherUserId });

builder.HasOne(x => x.Self)
.WithMany(x => x.Relationships)
.HasForeignKey(x => x.SelfId)
.OnDelete(DeleteBehavior.Restrict);

builder.HasOne(x => x.OtherUser)
.WithMany()
.HasForeignKey(x => x.OtherUserId)
.OnDelete(DeleteBehavior.Restrict);
}
}
public record ChatUser : IEntityTypeConfiguration<ChatUser>
{
public Guid Id { get; set; }

public required string Username { get; set; }
public required string Email { get; set; }
public required string HashedPassword { get; set; }

public Guid? RegistrationToken { get; set; }
public Guid? PasswordResetToken { get; set; }

public virtual ICollection<ChatChannel> Channels { get; set; } = new HashSet<ChatChannel>();
public virtual ICollection<ChatRelationship> Relationships { get; set; } = new HashSet<ChatRelationship>();

public void Configure(EntityTypeBuilder<ChatUser> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());
}
}


public record ChatRelationship : IEntityTypeConfiguration<ChatRelationship>
{
public Guid Id { get; set; }

public required Guid SelfId { get; set; }
public ChatUser Self { get; set; }

public required Guid OtherUserId { get; set; }
public ChatUser OtherUser { get; set; }

public required ChatRelationShipType Type { get; set; } = ChatRelationShipType.None;

public void Configure(EntityTypeBuilder<ChatRelationship> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());

builder.HasKey(f => new { User1Id = f.SelfId, User2Id = f.OtherUserId });

builder.HasOne(x => x.Self)
.WithMany(x => x.Relationships)
.HasForeignKey(x => x.SelfId)
.OnDelete(DeleteBehavior.Restrict);

builder.HasOne(x => x.OtherUser)
.WithMany()
.HasForeignKey(x => x.OtherUserId)
.OnDelete(DeleteBehavior.Restrict);
}
}
77 Replies
S-IERRA
S-IERRAOP16mo ago
Possibly what I assume could be happening is its trying to search by "SelfId" when the key is saved to "OtherUserId" ? tho the behaviour I'm expecting is its fetching any table that contains the value, both are keys so it shouldn't be a problem unless thats not how it works and its only searching by the 1st key
JakenVeina
JakenVeina16mo ago
EFC does not seem to be loading it at all for some reason
define this
Angius
Angius16mo ago
1. Don't use lazy loading 2. .Include() the related data you need 3. Or better yet, .Select() what you need
S-IERRA
S-IERRAOP16mo ago
I was told to use lazy loading any idea why I shouldn’t do it? I ve tried doing with include but same outcome
S-IERRA
S-IERRAOP16mo ago
Relationships shouldn’t be 0
Angius
Angius16mo ago
Because lazy loading will query the database multiple times So why do that, if you can query it once for all the data you need
JakenVeina
JakenVeina16mo ago
in a difficult to predict way with thread blocking
S-IERRA
S-IERRAOP16mo ago
I see, I can switching to load regularly that’s not an issue I guess, tho no idea why it still isn’t being loaded at all
Angius
Angius16mo ago
Since it's lazy-loaded, the related data will only ever be queried for when you access that property So, do you?
S-IERRA
S-IERRAOP16mo ago
I would believe so
S-IERRA
S-IERRAOP16mo ago
Angius
Angius16mo ago
Do you have UseLazyLoadingProxies() in your EF config?
S-IERRA
S-IERRAOP16mo ago
No
S-IERRA
S-IERRAOP16mo ago
Haha why
Angius
Angius16mo ago
Because I'm actively helping someone make lazy loading work Instead of discouraging them from using this shit
S-IERRA
S-IERRAOP16mo ago
I should of probably informed my self a bit more on this, just this is the way I've been taught the entire time
Angius
Angius16mo ago
And telling them to .Select()
S-IERRA
S-IERRAOP16mo ago
dw I ll switch it tho in some cases I have to use lazy loading i.e loading server members
Angius
Angius16mo ago
Why Why
S-IERRA
S-IERRAOP16mo ago
well youre not gonna load 100k entites at once are you ig paging does the job
Angius
Angius16mo ago
No, you paginate it
S-IERRA
S-IERRAOP16mo ago
fucking hell gonnaa be funny swapping the entire logic from lazy loading what would the difference between select and include be ? if at all
Angius
Angius16mo ago
.Include() means you're loading the entire entity and the entirety of related data Which is not always a good idea For example, if you want to display the user profile and their posts, if you do
var user = await _context.Users
.Where(u => u.Id == id)
.Include(u => u.Posts)
.FirstOrDefaultasync();
var user = await _context.Users
.Where(u => u.Id == id)
.Include(u => u.Posts)
.FirstOrDefaultasync();
it will load everything Including the user's password hash, their email, and all of their posts with their whole bodies and everything But you just need the user name, avatar, and the posts' IDs and titles So you can do
var user = await _context.Users
.Select(u => new UserProfileDto {
Name = u.Name,
Avatar = u.Avatar,
Role = u.Role.Name,
Posts = u.Posts.Select(p => new PostLinkDto {
Title = p.Title,
Id = p.Id,
Category = p.Category.Name
})
})
.FirstOrDefaultAsync();
var user = await _context.Users
.Select(u => new UserProfileDto {
Name = u.Name,
Avatar = u.Avatar,
Role = u.Role.Name,
Posts = u.Posts.Select(p => new PostLinkDto {
Title = p.Title,
Id = p.Id,
Category = p.Category.Name
})
})
.FirstOrDefaultAsync();
And it will get only that data
S-IERRA
S-IERRAOP16mo ago
oh yeah... thats alot better
Angius
Angius16mo ago
The difference between
SELECT * FROM Users
SELECT * FROM Users
and
SELECT u.Name, u.Avatar FROM Users u
SELECT u.Name, u.Avatar FROM Users u
S-IERRA
S-IERRAOP16mo ago
well depends on the scenario bu tyeah
S-IERRA
S-IERRAOP16mo ago
if i remeber correctly virtual on icollections marks the entity as being loaded lazily
Angius
Angius16mo ago
yep On any related data
S-IERRA
S-IERRAOP16mo ago
if (await context.Users.Where(x => x.Id == creatorId).Include(x=>x.Relationships).FirstOrDefaultAsync() is not { } creatorUser)
return NotFound();
if (await context.Users.Where(x => x.Id == creatorId).Include(x=>x.Relationships).FirstOrDefaultAsync() is not { } creatorUser)
return NotFound();
switched to this removing that virtual requires db update or no need
Angius
Angius16mo ago
No need It doesn't change anything about the database Just about how EF queries for items
S-IERRA
S-IERRAOP16mo ago
S-IERRA
S-IERRAOP16mo ago
ong im gonna shoot my self
S-IERRA
S-IERRAOP16mo ago
S-IERRA
S-IERRAOP16mo ago
its there
Angius
Angius16mo ago
The properties have to be virtual, because EF basically overrides
public virtual ICollection<Foo> Foos { get; set; }
public virtual ICollection<Foo> Foos { get; set; }
into a
private ICollection<Foo> _foos;
public override ICollection<Foo> Foos {
get => _dbContext.Foos.Where(f => f.ThingId == this.Id).ToList();
set => _foos = value;
}
private ICollection<Foo> _foos;
public override ICollection<Foo> Foos {
get => _dbContext.Foos.Where(f => f.ThingId == this.Id).ToList();
set => _foos = value;
}
S-IERRA
S-IERRAOP16mo ago
i think it could be bcause its querying by "SelfId" in this case its in OtherUserId
Angius
Angius16mo ago
huh
S-IERRA
S-IERRAOP16mo ago
fails to load them it could be my garbage config
public record ChatRelationship : IEntityTypeConfiguration<ChatRelationship>
{
public Guid Id { get; set; }

public required Guid SelfId { get; set; }
public ChatUser Self { get; set; }

public required Guid OtherUserId { get; set; }
public ChatUser OtherUser { get; set; }

public required ChatRelationShipType Type { get; set; } = ChatRelationShipType.None;

public void Configure(EntityTypeBuilder<ChatRelationship> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());

builder.HasKey(f => new { User1Id = f.SelfId, User2Id = f.OtherUserId });

builder.HasOne(x => x.Self)
.WithMany(x => x.Relationships)
.HasForeignKey(x => x.SelfId)
.OnDelete(DeleteBehavior.Restrict);

builder.HasOne(x => x.OtherUser)
.WithMany()
.HasForeignKey(x => x.OtherUserId)
.OnDelete(DeleteBehavior.Restrict);
}
}
public record ChatRelationship : IEntityTypeConfiguration<ChatRelationship>
{
public Guid Id { get; set; }

public required Guid SelfId { get; set; }
public ChatUser Self { get; set; }

public required Guid OtherUserId { get; set; }
public ChatUser OtherUser { get; set; }

public required ChatRelationShipType Type { get; set; } = ChatRelationShipType.None;

public void Configure(EntityTypeBuilder<ChatRelationship> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());

builder.HasKey(f => new { User1Id = f.SelfId, User2Id = f.OtherUserId });

builder.HasOne(x => x.Self)
.WithMany(x => x.Relationships)
.HasForeignKey(x => x.SelfId)
.OnDelete(DeleteBehavior.Restrict);

builder.HasOne(x => x.OtherUser)
.WithMany()
.HasForeignKey(x => x.OtherUserId)
.OnDelete(DeleteBehavior.Restrict);
}
}
Angius
Angius16mo ago
Well, OtherUser is not said to be related to Relationships, technically... So I wonder It shouldn't matter, I don't think
S-IERRA
S-IERRAOP16mo ago
is it not? eeeeeeeeeeee
Angius
Angius16mo ago
Angius
Angius16mo ago
.WithMany() is empty
S-IERRA
S-IERRAOP16mo ago
thought thats more of a way to reuse the existing relationship
Angius
Angius16mo ago
Again, I don't think it matters that you explicitly do that But it might
S-IERRA
S-IERRAOP16mo ago
public record ChatUser : IEntityTypeConfiguration<ChatUser>
{
public Guid Id { get; set; }

public required string Username { get; set; }
public required string Email { get; set; }
public required string HashedPassword { get; set; }

public Guid? RegistrationToken { get; set; }
public Guid? PasswordResetToken { get; set; }

public virtual ICollection<ChatChannel> Channels { get; set; } = new HashSet<ChatChannel>();
public ICollection<ChatRelationship> Relationships { get; set; } = new HashSet<ChatRelationship>();

public void Configure(EntityTypeBuilder<ChatUser> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());
}
}
public record ChatUser : IEntityTypeConfiguration<ChatUser>
{
public Guid Id { get; set; }

public required string Username { get; set; }
public required string Email { get; set; }
public required string HashedPassword { get; set; }

public Guid? RegistrationToken { get; set; }
public Guid? PasswordResetToken { get; set; }

public virtual ICollection<ChatChannel> Channels { get; set; } = new HashSet<ChatChannel>();
public ICollection<ChatRelationship> Relationships { get; set; } = new HashSet<ChatRelationship>();

public void Configure(EntityTypeBuilder<ChatUser> builder)
{
builder.Property(x => x.Id)
.HasDefaultValue(Guid.NewGuid());
}
}
because
Angius
Angius16mo ago
See what SQL your query generates Maybe EF doesn't know it has to join on both SelfId and OtherUserId
S-IERRA
S-IERRAOP16mo ago
this is the wrong thing
SELECT t."Id", t."Email", t."HashedPassword", t."PasswordResetToken", t."RegistrationToken", t."Username", r."SelfId", r."OtherUserId", r."Id", r."Type"
FROM (
SELECT u."Id", u."Email", u."HashedPassword", u."PasswordResetToken", u."RegistrationToken", u."Username"
FROM "Users" AS u
WHERE u."Id" = @__creatorId_0
LIMIT 1
) AS t
LEFT JOIN "Relationships" AS r ON t."Id" = r."SelfId"
ORDER BY t."Id", r."SelfId"
SELECT t."Id", t."Email", t."HashedPassword", t."PasswordResetToken", t."RegistrationToken", t."Username", r."SelfId", r."OtherUserId", r."Id", r."Type"
FROM (
SELECT u."Id", u."Email", u."HashedPassword", u."PasswordResetToken", u."RegistrationToken", u."Username"
FROM "Users" AS u
WHERE u."Id" = @__creatorId_0
LIMIT 1
) AS t
LEFT JOIN "Relationships" AS r ON t."Id" = r."SelfId"
ORDER BY t."Id", r."SelfId"
there
Angius
Angius16mo ago
Yeah, it only joins on SelfId
S-IERRA
S-IERRAOP16mo ago
Any clue why that is I assume its because of the SelectMany()
Angius
Angius16mo ago
Possibly
S-IERRA
S-IERRAOP16mo ago
tho if i were to write a second " .WithMany(x => x.Relationships)" it would error and say that relationship already exists
Angius
Angius16mo ago
Right, I was afraid this might be the case No clue why I wrote "afraid" as "fartaid" lmao
S-IERRA
S-IERRAOP16mo ago
dw haha, and hm weird I ll have to look around how I could make a "friendship" model so to say cuz it seems to be a pain in the ass
Angius
Angius16mo ago
The easiest way would be to have two collections MyRelationships and RelationshipsWithMe But that's more akin the followers system, not friendship where it has to be mutual
S-IERRA
S-IERRAOP16mo ago
Stack Overflow
How to Model Friendships Between IdentityUsers More Directly in Ent...
I'm trying to make a simple Social Network (MicroBlog) with ASP.NET Core 5+ with Entity Framework Core. I ran into a problem when modeling the friendships between the users and fetching friends of ...
S-IERRA
S-IERRAOP16mo ago
yeah found this but like exact same issue 1:1 just there is no replies
Angius
Angius16mo ago
I wonder if you can go around it with a manual join
S-IERRA
S-IERRAOP16mo ago
Possibly, how would I do that?
Angius
Angius16mo ago
.Join()
S-IERRA
S-IERRAOP16mo ago
programming is my passion
S-IERRA
S-IERRAOP16mo ago
manual join doesnt seem to be doing much, also its not really a good fix, i ll look around because i'm sure there is some weird ass way of doing this
Angius
Angius16mo ago
Try asking in #database
S-IERRA
S-IERRAOP16mo ago
@ZZZZZZZZZZZZZZZZZZZZZZZZZI'm thinking a hacky ass method I could write is if I made their relation ship many to many instead
Angius
Angius16mo ago
I mean, it already is many-to-many users-to-users
S-IERRA
S-IERRAOP16mo ago
yeah just written weirdly
Angius
Angius16mo ago
Except the join table has some added data Yeah
S-IERRA
S-IERRAOP16mo ago
yeah im a dumb ass the way i wrote this the fuck
Angius
Angius16mo ago
Nah, N-M with added data is fine
S-IERRA
S-IERRAOP16mo ago
mm i see
Angius
Angius16mo ago
It's just that EF seems to have issues with N-M where both sides of the relationship are the same
S-IERRA
S-IERRAOP16mo ago
Yeah
Angius
Angius16mo ago
And stored in the same collection
S-IERRA
S-IERRAOP16mo ago
I will make it M-M
JakenVeina
JakenVeina16mo ago
I've actually gotten in to work quite well PreviousVersionId and NextVersionId just had to configure the relationships manually, but the nav props worked fine, IIRC.
Accord
Accord16mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.

Did you find this page helpful?