Reputation: 177
I have this stricture:
public class ApplicationUser : IdentityUser
{
public Profile UserProfile { get; set; }
}
public class Profile
{
public Profile()
{
...
Events = new List<Event>();
...
}
public int Id { get; set; }
public string Name { get; set; }
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
public class Event
{
public int Id { get; set; }
public int WhereProfileId { get; set; }
public int WhoProfileId { get; set; }
[ForeignKey("WhereProfileId")]
[InverseProperty("Events")]
public virtual Profile WhereProfile { get; set; }
[ForeignKey("WhoProfileId")]
public virtual Profile WhoProfile { get; set; }
public string Text { get; set; }
public DateTime Posted { get; set; }
}
I have a request to get all the profile information, it includes getting the Event table and others. I am trying to do this: userProfile = _context.Users.Where(x => x.Id == currentUserId).Include(f => f.UserProfile).Select(e => e.UserProfile).Include(e => e.Events).FirstOrDefault();
But Events
empty in query result:
(i do not use fluent api)
Update:
Current configuration (still not work):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Profile>()
.HasMany(m => m.EventsIAmWho)
.WithOne(t => t.WhoProfile)
.HasForeignKey(m => m.WhoProfileId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Profile>()
.HasMany(m => m.EventsIAmWhere)
.WithOne(t => t.WhereProfile)
.HasForeignKey(m => m.WhereProfileId)
.OnDelete(DeleteBehavior.Restrict);
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
}
Classes:
public class Profile
{
public Profile()
{
EventsIAmWho = new List<Event>();
EventsIAmWhere = new List<Event>();
}
public int Id { get; set; }
public string Name { get; set; }
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public string AvatarUrl { get; set; }
public virtual ICollection<Event> EventsIAmWho { get; set; }
public virtual ICollection<Event> EventsIAmWhere { get; set; }
}
public class Event
{
public int Id { get; set; }
public int WhereProfileId { get; set; }
public int WhoProfileId { get; set; }
[ForeignKey("WhereProfileId")]
public virtual Profile WhereProfile { get; set; }
[ForeignKey("WhoProfileId")]
public virtual Profile WhoProfile { get; set; }
public string Text { get; set; }
public DateTime Posted { get; set; }
}
Upvotes: 0
Views: 47
Reputation: 34783
And which profile, WhoProfile or WhereProfile should an Event use to associate back to the UserProfile?
You need to explicitly set up the relationship between Profile and Event. EF can use conventions to work these out in simple cases but it will use the Type name, not the property name to try and resolve FK relationships.
I.e. if you had just:
public virtual Profile WhoProfile { get; set; }
with a field in the table called ProfileId it would probably resolve. The minute you add a second Profile property, or want to name the FK relative to the property name (WhoProfile) rather than the type (Profile) you will need to be explicit.
Using modelBuilder
on OnModelCreating
:
// EF6
modelBuilder.Entity<Profile>()
.HasMany(x => x.Events)
.WithRequired(x => x.WhoProfile)
.HasForeignKey(x => x.WhoProfileId);
//EF Core
modelBuilder.Entity<Profile>()
.HasMany(x => x.Events)
.WithOne(x => x.WhoProfile)
.Required()
.HasForeignKey(x => x.WhoProfileId);
If you want events for both the Who and Where profiles, then you need two collections in Profile. I.e.
// In Profile
public virtual ICollection<Event> EventsIAmWho { get; set; }
public virtual ICollection<Event> EventsIAmWhere { get; set; }
// Configuration
modelBuilder.Entity<Profile>()
.HasMany(x => x.EventsIAmWho)
.WithOne(x => x.WhoProfile)
.Required()
.HasForeignKey(x => x.WhoProfileId);
modelBuilder.Entity<Profile>()
.HasMany(x => x.EventsIAmWhere)
.WithOne(x => x.WhereProfile)
.Required()
.HasForeignKey(x => x.WhereProfileId);
Update: When querying the user profile and events you don't need to use Include
to dive down through the navigation properties. It is only used to eager load properties within an entity you want to select.
var profile = _context.Users
.Where(x => x.Id == currentUserId)
.Select(x => x.Profile)
.Include(e => e.EventsIAmWho)
.Single();
You can also navigate up the relationships. For instance, if you want a User Profile and it's events for a particular User, you don't have to start the query at the User, but could build the query from Profile:
var profile = _context.Profiles
.Where(x => x.User.Id == currentUserId)
.Include(e => e.EventsIAmWho)
.Single();
Update2: if you are updating a user profile to add an Event, then it is important to use that event's navigation properties. For instance with your example:
var eventToAdd = new Event()
{
WhereProfileId = model.ToProfileId,
Posted = DateTime.Now, WhoProfileId = userProfile.Id,
Text = model.Text
};
await _context.Events.AddAsync(eventToAdd);
await _context.SaveChangesAsync();
If you want to add the new event to the Profile that the DbContext is tracking:
var profile = _context.Profiles
.Where(x => x.User.Id == currentUserId)
.Include(e => e.EventsIAmWho)
.Single();
var toProfile = _context.Profiles
.Single(x => x.User.Id == model.ToProfileId);
var event = new Event()
{
WhereProfile = toProfile
Posted = DateTime.Now,
Text = model.Text
};
userProfile.EventsIAmWho.Add(event);
_context.SaveChanges();
We load the user profile, assuming it maps to event through Event's WhoProfile, eager loading the events. We also load the "ToProfile" value for the Where, not needing to worry about loading that user's events. We create the Event and set the references rather than the FK id's, and add it to the profile Events collection and save. Note that if the "WhereProfile" is the same user as the "WhoProfile" our Profile collection for EventsIAmWhere won't reflect an added event if it applies to both collections. (That scenario can be handled if necessary)
Upvotes: 1