I have two models. ApplicationUser:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public DateTime AccountCreationDate { get; set; }
public virtual ICollection<ProfileView> ProfilesViewed { get; set; }
}
And ProfileView:
public class ProfileView
{
public int Id { get; set; }
public DateTime ViewDate { get; set; }
public virtual ApplicationUser Viewer { get; set; }
public virtual ApplicationUser Viewee { get; set; }
}
Entity framework seems to have created my tables correctly. I can do the following and retrieve a user's ProfileViews:
db.ProfileViews.Where(p => p.Viewer.Id == currentUser.Id);
My problem is that I can't seem to do the following:
db.Users
.Where(u => u.Id == currentUser.Id)
.Include(u => u.ProfilesViewed);
The above returns null for that user, even though it is a Viewer and a Viewee on several ProfileView.
I ran a foreach on all my users, none of them seem to have any ProfilesViewed if I query them from the Users table with Include. I can only retrieve ProfileViews from the ProfileViews table...
Anyone has any idea how to fix this?
Since you did not mention how the ProfileView.Viewer is related to the ApplicationUser.ProfileViewed EF thinks that they are not related(if you check your DB you can see another FK created in the ProfileView for the ApplicationUser.ProfileViewed collection). So adding instances to ProfileView does not effect the User.ProfilesViewed.
Add this code to the Context class, to specify that each ApplicationUser is related to many ProfileView through ProfilesViewed collection.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProfileView>().HasRequired(x => x.Viewer)
.WithMany(x => x.ProfilesViewed);
}
The relationships probably need to be explained to be created correctly. I think it's wrong because you have two relationships from ProfileView to ApplicationUser. See the section "Configuring Unconventional Foreign Key Names" in this MSDN article for details of how to configure unconventional relationships using EF Code First.
Related
I have two entities that represent a User of the web application, and a Participant in the podcast that the web application is all about. A user of the web page has a profile, can log in, can leave comments etc. A Participant is linked from the episode objects they appear in, has a bio, a picture etc. It is possible to be both a Participant and a User at the same time, but it is also possible to be just a user, or just a participant.
I'm struggling to model this in EF Core 3.1. If it matters, I am also using .Net Core 3.0 for this project, and the database is Postgresql (using the Nuget package Npgsql.EntityFrameworkCore.PostgreSQL v3.1.0).
On both sides this relationship should be nullable/non-required. The entities are pretty simple (all non-importart properties omitted):
User:
public class User
{
public Guid UserId { get; set; }
public Participant Participant { get; set; }
public Guid ParticipantId { get; set; }
}
Participant:
public class Participant
{
public Guid ParticipantId { get; set; }
public User User { get; set; }
public Guid UserId { get; set; }
}
I am trying to use the Fluent API to configure the relationships - this seems to be where it breaks down.
User config:
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> user)
{
user.ToTable("users");
user.HasKey(u => u.UserId);
user.HasOne(u => u.Participant)
.WithOne(p => p.User)
.HasForeignKey<Participant>(p => p.UserId);
}
}
Participant config:
public class ParticipantConfiguration : IEntityTypeConfiguration<Participant>
{
public void Configure(EntityTypeBuilder<Participant> participant)
{
participant.ToTable("participants");
participant.HasKey(p => p.ParticipantId);
participant.HasOne<User>(p => p.User)
.WithOne(u => u.Participant)
.HasForeignKey<User>(u => u.ParticipantId);
}
}
Now I realize that you should only configure one side of the relationship - at least that is how I interpret what I have read. I have just included the above for completeness; I have tried configured both sides at once as above, I have tried doing it only on the User side, and only on the Participant side. In every combination, the code compiles, and the application starts up, but when I try actually adding a User to the database through the DbContext, I get the same exception:
System.InvalidOperationException: 'The child/dependent side could not be determined for the one-to-one relationship between 'Participant.User' and 'User.Participant'. To identify the child/dependent side of the relationship, configure the foreign key property. If these navigations should not be part of the same relationship configure them without specifying the inverse. See http://go.microsoft.com/fwlink/?LinkId=724062 for more details.'
Now, these are two completely independent objects that happen to know of each other, so I am not sure the mindset of child/dependent is accurate, but I am willing to ignore that detail to bend to EF Core's will; However, I can't figure out how to let me get this working without the exception.
TL;DR:
A User can have a Participant link.
A Participant can have a User link.
It is perfectly fine for either for that link to be NULL.
How do I configure EF Core for this?
Thanks for any insights!
First thing to do i think is to change foreign keys into nullables.
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid UserId { get; set; }
public Participant Participant { get; set; }
public Guid? ParticipantId { get; set; }
}
public class Participant
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid ParticipantId { get; set; }
public User User { get; set; }
public Guid? UserId { get; set; }
}
And then keep your configurations intact. My sample working configuration :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasOne(t => t.Participant)
.WithOne(t => t.User)
.HasForeignKey<Participant>(t => t.UserId);
modelBuilder.Entity<Participant>().HasOne(t => t.User)
.WithOne(t => t.Participant)
.HasForeignKey<User>(t => t.ParticipantId);
base.OnModelCreating(modelBuilder);
}
I would double check that your configuration is being called. Everything looks like it should work.
I'm trying to create a 'one to many' relation between the classes 'ApplicationUser' and one recently created called 'Issue'.
So, in Models / IdentityModels.cs / ApplicationUser i added this property:
public ICollection<Issue> Issues { get; set; }
And Issue.cs has this code:
namespace Test.Models
{
public class Issue
{
public int Id { get; set; }
public ApplicationUser Courier { get; set; }
public ApplicationUser Customer { get; set; }
}
}
I'm using automatic migrations. So, after building and running 'update-database', the Issues table was created with these fields:
Id
ApplicationUser_Id
CourierId
CustomerId
My question is why was the field 'ApplicationUser_Id ' created and how can i prevent it?
The problem is that EF thinks you actually want three one-to-many relationships between Issue and ApplicationUser:
one for ICollection<Issue> Issues on ApplicationUser (ApplicationUser_Id)
one for ApplicationUser Courier on Issue (CourierId)
one for ApplicationUser Customer on Issue (CustomerId)
(Note that EF allows to define relationships from either side.)
If you want that ApplicationUser.Issues contains all Issues of this User, whether he is a Courier or Customer, you will need the additional ApplicationUser_Id key. Configuring EF so that this works will be quite a pain.
Maybe a simpler solution will do: introduce two collections on ApplicationUser.
public ICollection<Issue> CourierIssues { get; set; }
public ICollection<Issue> CustomerIssues { get; set; }
And then configure the backlinks in the ModelBuilder using the fluent API to eliminate the ApplicationUser_Id key:
modelBuilder.Entity<ApplicationUser>().HasMany(au => au.CourierIssues).WithOptional(i => i.Courier);
modelBuilder.Entity<ApplicationUser>().HasMany(au => au.CustomerIssues).WithOptional(i => i.Customer);
Because you have two foreign keys to ApplicationUser, Courier and Customer, but only one collection referencing Issue on ApplicationUser. EF has no way of know which foreign key it should line up with, so it just created a new one. To handle this properly, you need to utilize fluent config:
public class ApplicationUser
{
...
public class Mapping : EntityTypeConfiguration<ApplicationUser>
{
HasMany(m => m.Issues).WithRequired(m => m.Customer);
}
}
Then, in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ApplicationUser.Mapping());
}
The problem here of course, is that you are likely wanting to track collections for both the Customer and Courier collections. For that, you need two collections:
public virtual ICollection<Issue> CustomerIssues { get; set; }
public virtual ICollection<Issue> CourierIssues { get; set; }
Then, the following fluent config:
HasMany(m => m.CustomerIssues).WithRequired(m => m.Customer);
HasMany(m => m.CourierIssues).WithRequired(m => m.Courier);
I have two DbContext in my application, which configure some models with Fluent API. One model of my first DbContext has a foreign key to a second model configured in my second DbContext.
public class UserData
{
public double Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName {
get {
return $"{this.FirstName} {this.LastName}";
}
}
public string Adress1 { get; set; }
public virtual BaseUserTreeData BaseUserTree { get; set; }
public double? BaseUserTreeId { get; set; }
public virtual List<DeviceData> Devices { get; set; }
}
The model BaseUserTreeData is my foreign property configured in my second DbContext.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BaseUserTreeData>().ToTable("sw_data_baseusertree");
modelBuilder.Entity<BaseUserTreeData>().Property(baseusertree => baseusertree.Id).HasColumnName("baseusertree_ID");
modelBuilder.Entity<BaseUserTreeData>().Property(baseusertree => baseusertree.Label).HasColumnName("label");
modelBuilder.Entity<BaseUserTreeData>().Property(baseusertree => baseusertree.ParentTreeId).HasColumnName("baseUserTree_ID_parent");
modelBuilder.Entity<BaseUserTreeData>().HasKey(baseusertree => baseusertree.Id);
modelBuilder.Entity<BaseUserTreeData>()
.HasOptional(tree => tree.ParentTree)
.WithMany(tree => tree.ChildTrees)
.HasForeignKey(tree => tree.ParentTreeId);
}
When I use UserData with my UserDbContext for the first time, the OnModelCreating of my second DbContext is not called, so BaseUserTreeData mapping is not executed, and the query generated by Entity Framework 6 is wrong. I saw that I can share EntityConfiguration in separate classes, but is there a way to tell to EF6 to call every OnModelCreating of all my DbContext?
I think you're in wrong path.This is not a recommended way of handling Context.If there is too strong relationship between models, you have to concentrate the models inside one unique context.Otherwise you'll have to face so many issues in the future.So my advice is to use the pattern which EF team suggested below.
This is the way Microsoft EF Team has suggested :
When working with Web applications, use a context instance per
request.
You can read more about context handling using below articles :
Working with DbContext
Managing DbContext the right way with Entity Framework
Some of the entities in my application have 4 audit properties on them:
public virtual DateTime WhenAdded { get; set; }
public virtual DateTime? WhenUpdated { get; set; }
public virtual User AddedBy { get; set; }
public virtual User UpdatedBy { get; set; }
I am using a code first approach and have the following extension method to map the user properties:
public static void MapAuditFields<T>(this EntityTypeConfiguration<T> configuration) where T : class, IAuditable
{
configuration.HasOptional<User>(e => e.AddedBy)
.WithOptionalDependent()
.Map(a => a.MapKey("AddedByUserId"));
configuration.HasOptional<User>(e => e.UpdatedBy)
.WithOptionalDependent()
.Map(a => a.MapKey("UpdatedByUserId"));
}
This is working fine in most cases, but not on the User class, which of course has a recursive relationship with itself. I have seen various posts on the internet suggesting that entity framework has a bug when you try to customise join table column names in this scenario, for example:
Self-referencing many-to-many recursive relationship code first Entity Framework
and
http://social.msdn.microsoft.com/Forums/en-US/f058097d-a0e7-4393-98ef-3b13ab5b165d/code-first-sequence-contains-more-than-one-matching-element-exception-when-generating-schema?forum=adonetefx
The error I am getting is "Sequence contains more than one matching element".
Does anyone know if this has been fixed in entity framework 6?
Many thanks.
Use WithMany() instead of WithOptionalDependent() as a user can add or update multiple other users
Class:
public class User
{
public int Id { get; set; }
public virtual User AddedBy { get; set; }
public virtual User UpdatedBy { get; set; }
}
Fluent API calls:
modelBuilder.Entity<User>()
.HasOptional( u => u.AddedBy )
.WithMany()
.Map( fkamc => fkamc.MapKey( "AddedByUserId" ) );
modelBuilder.Entity<User>()
.HasOptional( u => u.UpdatedBy )
.WithMany()
.Map( fkamc => fkamc.MapKey( "UpdatedByUserId" ) );
Results:
I can't seem to make this work at all
class Member
{
public virtual IList<Member> Friends { get; set; }
[Key]
public int MemberId { get; set; }
public string Name{ get; set; }
}
I tried adding Mappings but in vain. Is there a way to do so with CTP5?
By convention, Code First will take uni-directional associations as one to many. Therefore you need to use fluent API to let Code First know that you want to have a many to many self referencing association:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().HasMany(m => m.Friends).WithMany().Map(m =>
{
m.MapLeftKey("MemberId");
m.MapRightKey("FriendId");
m.ToTable("MembersFriends");
}
);
}
If I am correct you can influence the many to many table name with this code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().HasMany(m => m.Friends).WithMany().Map(m =>
{
m.MapLeftKey("MemberId");
m.MapRightKey("FriendId");
m.ToTable("MembersFriends");
}
);
}
hope this helps.
You can get this to work in EF 4 CTP5 using Model-First, but the CTP5 Code First has too many bugs with self-referential and polymorphic query configurations to use Code First for such scenarios. Morteza Manavi (see other answer) has documented several of them on his blog.
I wanted to get this done without having to write fluent API code. So here is my take.
The following example trying to save whomever other users profiles the user visited and who visited that user profile. Sadly, the following example doesn't support extra properties other than two ids of the visiting and the visited user.
The navigation properties had been linked using the InversePropertyAttribute.
see more about it in entityframework.net and entityframeworktutorial.net
Model ↴
public class User
{
[InverseProperty(nameof(User.VisitingUsers))]
public virtual List<User> VisitedUsers { get; set; }
[NotMapped]
public long? VisitedUsersCount { get { return this.VisitedUsers == null ? 0 : this.VisitedUsers.Count(); } }
[InverseProperty(nameof(User.VisitedUsers))]
public virtual List<User> VisitingUsers { get; set; }
[NotMapped]
public long? VisitingUsersCount { get { return this.VisitingUsers == null ? 0 : this.VisitingUsers.Count(); } }
}
Generated Migration Code ↴
CreateTable(
"dbo.UserUsers",
c => new
{
User_Id = c.Long(nullable: false),
User_Id1 = c.Long(nullable: false),
})
.PrimaryKey(t => new { t.User_Id, t.User_Id1 })
.ForeignKey("dbo.Users", t => t.User_Id)
.ForeignKey("dbo.Users", t => t.User_Id1)
.Index(t => t.User_Id)
.Index(t => t.User_Id1);
Your example is not a many-to-many relationship, it is more of a recursive relationship.
I am not sure how to fix it. But the problem with your code is that your will get two fields on the same row with the same name. MemberId for the id of the row and MemberId for the id of the friend.
Edit
Try doing it like this:
class Member
{
[Key]
public int MemberId { get; set; }
public string Name { get; set; }
public virtual IList<FriendRelationship> Friends { get; set; }
}
class FriendRelationship
{
[Key]
public int RelationshipId { get; set; }
public Member Friend { get; set; }
}