I'm working on a EF5 code-first database and it is giving me a lot of trouble on a foreign key. Also sorry if this is already answered somewhere else but I looked at countless of questions on here already and none helped so far.
I have this user class
public class User
{
[Key()]
[HiddenInput(DisplayValue=false)]
public int UserId { get; set; }
[Required]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[ScaffoldColumn(false)]
public CustomClass Custom { get; set; }
}
And this custom class
public class CustomClass
{
[Key()]
[HiddenInput(DisplayValue = false)]
public int CustomClassId { get; set; }
[Required]
public string Name { get; set; }
}
And this DataContext
public class SilkDbContext3 : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<CustomClass> CustomClasses { get; set; }
}
Now, the problem is I can save a PoseAnimation perfectly through the controller. I can also post and update an user with the basic properties (Username, Password), but when I add a custom class to the user, it saves it as a foreign key in the database row, but when I want to retrieve the user, the dbcontext just gives me back null for the custom class (whether I'm retrieving through GET or intellisense). Can anyone help me out here?
You'll need to either explicitly or eagerly load the related data, or mark the Custom property as virtual if you're using lazy loading.
Related
I am trying to create a discussion board using Identity where ApplicationUsers can create, save, hide, and comment on Posts. I was able to get Posts and Comments working without data annotations or overriding OnModelCreating as follows:
Post.cs:
public class Post
{
public int ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[DataType(DataType.DateTime)]
public DateTime CreationDate { get; set; }
public ApplicationUser OriginalPoster { get; set; }
public int Upvotes { get; set; }
public int Downvotes { get; set; }
public int VoteScore { get; set; }
public ICollection<Comment> Comments { get; set; }
}
Comment.cs:
public class Comment
{
public int ID { get; set; }
public string Content { get; set; }
public ApplicationUser Commenter { get; set; }
[DataType(DataType.DateTime)]
public DateTime CreationDate { get; set; }
public int Upvotes { get; set; }
public int Downvotes { get; set; }
public int VoteScore { get; set; }
}
ApplicationDbContext.cs:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
}
But when I extend IdentityUser to add my own custom fields:
public class ApplicationUser : IdentityUser
{
public ICollection<Post> CreatedPosts { get; set; }
public ICollection<Post> SavedPosts { get; set; }
public ICollection<Post> HiddenPosts { get; set; }
}
Add-Migration returns with error:
"Unable to determine the relationship represented by navigation
'ApplicationUser.CreatedPosts' of type 'ICollection'. Either
manually configure the relationship, or ignore this property using the
'[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in
'OnModelCreating'."
Why is EF Core able to determine the relationship between a Post and its Comments but not an ApplicationUser and its created/saved/hidden Posts? I understand that I will have to specify the relationship either by using data annotations or overriding OnModelCreating but I am unsure of how to go about doing this. Any amount of help would be very much appreciated.
The reason is because you have multiple collection properties referencing the same model, Post. This type of situation you need to specifically tell EF Core which foreign properties each of CreatedPosts, HiddenPosts and SavedPosts to reference from Post. Given you only have one ApplicationUser foreign property named OriginalPoster, that would be impossible because there are no other properties HiddenPosts and SavedPosts would reference. You would only be able to reference one by configuring it like this.
builder.Entity<ApplicationUser>().HasMany(s => s.CreatedPosts)
.WithOne(f => f.OriginalPoster);
Now, which properties do the other two (HiddenPosts and SavedPosts) reference? I hope you see the problem here.
But assuming you have another type of poster defined in your Post model like this.
public ApplicationUser HiddenPoster {get;set;}
You make the collection it belongs to reference it as well.
builder.Entity<ApplicationUser>().HasMany(s => s.HiddenPosts)
.WithOne(f => f.HiddenPoster);
But you don't, so this approach would not work because it's only one type of poster you have in your Post. I would suggest you redefined your model to have an enum in Post with values Created,Hidden and Saved.
public enum PostStatus
{
Created,
Hidden,
Saved
}
Then define the status in the Post model like this.
public PostStatus Status {get;set;}
So that in your ApplicationUser, you do not have to define multiple collections, you only have Posts;
public class ApplicationUser : IdentityUser
{
public ICollection<Post> Posts {get;set;}
}
and you can then filter which post is created, hidden or saved using the Status enum property from Post.
I'm using Entity Framework 6.1.1 and I have a Users table and a User_Documents table (1:many). I already had a navigation property from User_Documents to User were things were working fine.
public partial class User_Document
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_Document_ID { get; set; }
public long User_ID { get; set; }
[ForeignKey("User_ID")]
public virtual User User { get; set; }
}
I added a navigation property from Users to User_Documents
public partial class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_ID { get; set; }
[StringLength(50)]
public string Username { get; set; }
public virtual List<User_Document> Documents { get; set; }
}
and now I'm getting an error when I try to run the application:
System.Data.Entity.ModelConfiguration.ModelValidationException: One or
more validation errors were detected during model generation:
User_Documents: Name: Each member name in an EntityContainer must be
unique. A member with name 'User_Documents' is already defined.
Of course there is a table called User_Documents but no other property with that name. I'm not sure what's it getting confused by. Maybe it's taking the table name "User" and the property name "Documents" and trying to create something called "User_Documents" out of it? If I rename it to from Documents
to Some_Documents like this
public virtual List<User_Document> Some_Documents { get; set; }
then I get a different error stating
System.InvalidOperationException: The model backing the
'PipeTrackerContext' context has changed since the database was
created. Consider using Code First Migrations to update the database
So I run Add-Migration and I get this:
public override void Up()
{
AddColumn("dbo.User_Documents", "User_User_ID", c => c.Long());
CreateIndex("dbo.User_Documents", "User_User_ID");
AddForeignKey("dbo.User_Documents", "User_User_ID", "dbo.Users", "User_ID");
}
Why is it trying to add a new column called User_User_ID? Why can't I just add the Document navigation property like I want?
use InverseProperty Like this :
public partial class User_Document
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_Document_ID { get; set; }
public long User_ID { get; set; }
[ForeignKey("User_ID")]
[InverseProperty("Documents")]
public virtual User User { get; set; }
}
And :
public partial class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_ID { get; set; }
[StringLength(50)]
public string Username { get; set; }
[InverseProperty("User")]
public virtual List<User_Document> Documents { get; set; }
}
Why is it trying to add a new column called User_User_ID? Why can't I
just add the Document navigation property like I want?
By convention, it will create the foreign key as tablename_columnName which is User_User_ID. This happens when you remove the [ForeignKey("User_ID")] attribute OR don't have foreign key property.
If you change the property name Documents to something else (likeUserDocuments) you wont face this conflict of names.
I have two model classes one is ApplicationUser and the second is Appointment. Application user includes all users that use the application, in my case, Doctors and Data entry operators. Doctors will be assigned to each appointment and Data entry operators will be making this log to DB. I want to map both these users with appointment. I have tried something like this
public class Appointment
{
public int AppointmentID { get; set; }
public DateTime Date { get; set; }
public int DoctorID { get; set; }
[ForeignKey("DoctorID")]
public virtual ApplicationUser Doctor { get; set; }
public int SystemUserID { get; set; }
public virtual ApplicationUser SystemUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
public string Email { get; set; }
public string Mobile { get; set; }
public string FirstNsme { get; set; }
public string LastName { get; set; }
}
But this throws an error
Appointment_Doctor_Target_Appointment_Doctor_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'DoctorID' on entity 'Appointment' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Appointment_Doctor'.
Can anyone point out why this error is occurring and what is the correct approach to this problem?
IdentityUser as all entities in asp.net identity entity framework have string as key. You are trying to map to an int. So either use Guids as foreign keys in your Appointment entity
public class Appointment
{
[Key]
public int AppointmentID { get; set; }
public DateTime Date { get; set; }
public string DoctorID { get; set; }
[ForeignKey("DoctorID")]
public virtual ApplicationUser Doctor { get; set; }
public string SystemUserID { get; set; }
[ForeignKey("SystemUserID ")]
public virtual ApplicationUser SystemUser { get; set; }
}
or change the type of Ids in identity classes to int. You can find help here.
There are multiple issue in your classes.
What is DoctorID? Where it is defined?
You need to first focus on establishing correct relationship between your entities logically.
I think your Appointment class need not contain SystemUserID who added an appointment.
Second if you wanted to share some properties between two user types than create a common class and derive in Doctor and SystemUser.
Add DoctorId into Doctor table along with specific details pertaining to Doctor e.g. Specialty.
SystemUser adds a appointment so the table should contain data related to that i.e. doctorId and appointmentId.
Update:
Based on your comment, you could do something like this. Note its for reference only, you are better person to define a better DB Schema.
public class Appointment
{
public int AppointmentID { get; set; }
public DateTime Date { get; set; }
public int DoctorID { get; set; }
[ForeignKey("ApplicationUserId")]
public virtual ApplicationUser Doctor { get; set; }
public int SystemUserID { get; set; }
[ForeignKey("ApplicationUserId")]
public virtual ApplicationUser SystemUser { get; set; }
}
public class ApplicationUser
{
public int ApplicationUserId { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public string FirstNsme { get; set; }
public string LastName { get; set; }
public UserType UserType { get; set; }
}
public enum UserType
{
Doctor,
SystemUser
}
FURTHER AND MORE COMPLEX ERROR:
I had this error multiple times across 4 linked tables.
Each table had composite keys of 3 - 7 fields,
and one table referenced its own 3-field key with a different mix of its own columns.
I struggled for ages with fixing one sequence of fields (which does fix the error as mentioned in other posts) only to have knock-on effect in other entities.
The solution:
Align all linked tables' FK fields in order of reducing occurrence
ORIGINALLY:
AFTER KEY FIELDS WERE ALIGNED:
And re-ordered all anonymous FK objects in FluentAPI in the DbContext to match the new order.
This fixed all headaches.
Consider this Poco:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
}
Now i want to implement a follow technique where a user may follow other users so basically its self Many to Many relationship
problem is i don't know how exactly i can achieve this in Entity Framework Code-First ?
I thought of a linker Table :
public class UserFollow
{
public int Id { get; set; }
public int Follower { get; set; }
public int Following { get; set; }
public DateTime FollowDate { get; set; }
}
i want to be able to get All Followers and Following from every User Object?
This is quite simple using EF code-first as you only need the User POCO:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
public ICollection<User> FollowedUsers { get; set; }
}
The collection means that a User is related to other users.
PS: I noted you added a timestamp in your solution example. To achieve that you should still add the collection changing the generic type to whatever suits your needs.
Hope it helps.
I'm new to asp.net, mvc3 and entity framework.
I'm trying to develop a mvc3 programm with entity framework and code-first.
So I have two classes with a many-to-many relationship.
One class called "User" the other one is "Course".
public class Course : IValidatableObject
{
[...]
public virtual ICollection<User> Users { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<Course> Courses { get; set; }
[...]
}
So, this works. But now I need an additional field which safes the status of the registration for a course.
Is there an easy way I don't know?
I tried it this way:
public class Course : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class CourseUser
{
[Key, ForeignKey("Course"), Column(Order = 0)]
public int Course_ID { get; set; }
[Key, ForeignKey("User"), Column(Order = 1)]
public string User_ID { get; set; }
public int Status { get; set; } //{ pending, approved }
public virtual Course Course { get; set; }
public virtual User User { get; set; }
}
But this makes it much more difficult to add or edit related data.
For example I didn't managed it yet to automatically add the user who created the course to the CourseUsers table.
No there is no easier way to do that. Once you add any additional field to your junction table it must be mapped as entity to allow you access to that field. It is not pure many-to-many relation any more. It is a new entity in your model with two one-to-many relations.