Note: for this question, I've simplified the models a bit.
Im working on an ASP.NET Core MVC application (with Entity Framework Core). I have three models. I have an ApplicationUser (that extends from IdentityUser), I have an Activity model and I have a 'SignUp' model that has a foreign key to an activity and a foreign key to ApplicationUser. At least, that's the idea. The thing is, Entity Framework recognices the Activity FK as a FK, but not the applicationuser. That just becomes another int in the database without any FK constraints.
I've tried a lot of things that I found on the internet, but I can't get it to work. This is what I currently have (shortened for clarity):
Activity:
public class Activity
{
public Activity()
{
SignUps = new HashSet<SignUp>();
}
public int ActivityID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Naam")]
public string Name { get; set; }
public virtual ICollection<SignUp> SignUps { get; set; }
}
ApplicationUser:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Activities = new HashSet<Activity>();
}
[Required]
[Display(Name = "Voornaam")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Achternaam")]
public string LastName { get; set; }
public virtual ICollection<Activity> Activities { get; set; }
}
SignUp:
public class SignUp
{
public int SignUpID { get; set; }
[Required]
public int ActivityID { get; set; }
[ForeignKey("ApplicationUser")]
public int ApplicationUserID { get; set; }
[Required]
public string Status { get; set; }
}
Note: I first did not have the [ForeignKey] attribute (like with activity), but it's there because I was trying to get it working.
As I said above, the Activity FK in SignUp works fine, the Applicationuser FK doesn't. Entity Framework just generates an int in the database without any FK constraints and when scaffolding CRUD pages, it's also not recognized as an FK.
I'm quite new to ASP.net MVC and EF and I have the feeling I'm overlooking something very simple. Facepalm moment incoming?
Thanks!
The Activity relationship works because you have navigation properties involved there. Activity has an ICollection<SignUp>, so EF is binding to ActivityID on SignUp to maintain that collection. There is nothing similar being done with ApplicationUser. You just have an property called ApplicationUserID; EF will not do anything with that because it has no idea it should.
Just add a navigation property and you'll be good:
[ForeignKey("ApplicationUser")]
public int ApplicationUserID { get; set; }
public ApplicationUser ApplicationUser { get; set; }
EDIT
Actually, after looking at your code a bit more, I think you may have just messed up with the ICollection<Activity> on ApplicationUser. There's no direct relationship between ApplicationUser and Activity. That relationship comes via SignUp, which is basically an M2M with a payload. As a result, you should have the following instead:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Activities = new HashSet<Activity>();
}
[Required]
[Display(Name = "Voornaam")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Achternaam")]
public string LastName { get; set; }
public virtual ICollection<SignUp> SignUps { get; set; }
}
Then, it would function exactly the same as your Activity relationship does currently.
Related
So let's say I have an ApplicationUser : IdentityUser model class that has the identity relations and it has a String ID by default and roles assigned when the user signs up.
ApplicationUsers will have different roles for example Student and Library.
The Library will have a list of books while the Student will have list of orders.
Now I want to create a List of another model which will have the name Orders, but the Orders model class will have two UserIds as foreign keys from ApplicationUser.
ApplicationUser: IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Libraries> Libraries { get; set; }
public List<Orders> Orders { get; set; }
}
Since the ApplicationUser is connected with ASPNetRoles, what I want to achieve on Orders model class is that I want to have different StudentId and LibraryId from the same table which is ApplicationUser:
public class Orders
{
[Key]
public id OrderId {get; set;}
[ForeignKey("StudentId")]
public string StudentId { get; set; }
public ApplicationUser Student {get; set;}
[ForeignKey("LibraryId")]
public string LibraryId {get; set;}
public ApplicationUser Library {get; set;}
}
Is there any way I can achieve this? What are the best solutions for this case? I did try ICollection and list but still same. Any suggestion about this would be great.
When I run Add-Migrations, I get this error:
System.InvalidOperationException: Unable to determine the relationship represented by navigation 'ApplicationUser.OrdersLists' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Thank you.
Ok, something in you model doesn't look quite right...
In your User class you have a collection of Orders and an collection of a class called Library. Yet within your Order class you have a property called Library, but point that at an ApplicationUser class?
EF does support having multiple references to the same class, though you need to explicitly tell it what the FK names would be. EF's default convention is to base FK names on the type of the navigation property, not the name of the navigation property.
Take the following:
public class Order
{
....
public int CreatedById { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
public int LastModifiedById { get; set; }
public virtual ApplicationUser LastModifiedBy { get; set; }
}
Here by default EF would want to use "ApplicationUser_Id" or "ApplicationUserId" as a FK name for both of the two navigation properties, settling on something like "ApplicationUser_Id" and "ApplicationUser_Id1" if left to its own devices with the schema. In this situation we would need to configure it to use our desired FK properties:
[ForeignKey("CreatedBy")]
public int CreatedById { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
[ForeignKey("LastModifiedBy")]
public int LastModifiedById { get; set; }
public virtual ApplicationUser LastModifiedBy { get; set; }
or the FK attribute can be put on the navigation property:
public int CreatedById { get; set; }
[ForeignKey("CreatedById")]
public virtual ApplicationUser CreatedBy { get; set; }
public int LastModifiedById { get; set; }
[ForeignKey("LastModifiedById")]
public virtual ApplicationUser LastModifiedBy { get; set; }
The ForeignKey Attribute is a bit weird, as it represents either "I am the FK of ..." if on the FK property, or it represents "My FK is ...." if on the navigation property.
With EF Core the FK property can be left off and treated by EF as a shadow property which is recommended to avoid having two sources of truth in the entity.
[ForeignKey("CreatedById")]
public virtual ApplicationUser CreatedBy { get; set; }
[ForeignKey("LastModifiedById")]
public virtual ApplicationUser LastModifiedBy { get; set; }
In situations where you want bi-directional references in the other side of the relationship, you may need to map those out. For instance if I want a "CreatedOrders" in my ApplicationUser class:
public class ApplicationUser
{
// ...
public virtual ICollection<Order> CreatedOrders { get; set; } = new List<Order>();
}
Now it's generally a good idea to tell EF what to relate this back to since Order has two references to the application user. Again, this can be done on either side of the relationship. So in the case of back in our Order class:
[ForeignKey("CreatedById"), InverseProperty("CreatedOrders")]
public virtual ApplicationUser CreatedBy { get; set; }
This tells EF that CreatedBy on this record is the link to use when accessing the orders for CreatedOrders.
Back to your example it is a bit confusing why ApplicationUser would contain a collection of Libraries while an Order expects a "library" to be a User.
I am having issues trying to map two fields that are foreign keys into the same table. The use case is for a modifier and creator. My class already has the Ids, and then I wanted to add the full User object as virtual.
I am using a base class so that each of my tables have the same audit fields:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
[ForeignKey("CreatedById")]
public virtual User CreatedByUser { get; set; }
[ForeignKey("ModifiedById")]
public virtual User ModifiedByUser { get; set; }
}
The child class is very simple:
public class CircleUserSubscription : Entity
{
[Required]
public long Id { get; set; }
public long SponsorUserId { get; set; }
[ForeignKey("SponsorUserId")]
public virtual User User { get; set; }
public long TestId { get; set; }
[ForeignKey("TestId")]
public virtual User Test { get; set; }
}
This is a standard junction table.
When I try to generate the migration, I am getting errors that I don't understand fully.
Unable to determine the relationship represented by navigation property 'CircleUserSubscription.User' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I tried what this answer had, but the code is basically the same: https://entityframeworkcore.com/knowledge-base/54418186/ef-core-2-2---two-foreign-keys-to-same-table
An inverse property doesn't make sense since every table will have a reference to the user table.
For reference, here is the User entity:
public class User : Entity
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I am hoping you all can help me out, TIA :)
EDIT: One thing to note, all of this worked fine when the entity class was as follows:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
}
It was only after I added the entity that things went awry.
So, I was trying to get something with EF core. I'm doing code-first as it's best for me (I prefer it also cause no database has to be provided for users). Anyways, I'm going to have a big database with loads of relations, so I have to use foreign keys.
However, the foreign key collection is always null, even if in the database it's linked. I got these 2 models:
User:
[Table("users")]
public class User
{
[Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("name")]
[StringLength(15, MinimumLength =2)]
[Required]
public string Name { get; set; }
[Column("password")]
[StringLength(100)]
[Required]
public string Password { get; set; }
[Column("email")]
[StringLength(30, MinimumLength =6)]
[Required]
public string Email { get; set; }
public List<UserActivityPoints> Activitypoints { get; set; }
}
UserActivityPoints:
[Table("activitypoints")]
public class UserActivityPoints
{
[Key]
[Column("type")]
public int Type { get; set; }
[Column("amount")]
public int Amount { get; set; }
[Key]
[Column("user_id")]
public User User { get; set; }
}
So, one thing that already was weird is, in the database, the user_id column becomes UserId, however it is correctly linked to the users, as shown below:
A picture of PHPMyAdmin showing it's linked correctly
In my database context, I have the following code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("User");
modelBuilder.Entity<UserActivityPoints>().ToTable("activitypoints");
}
I run the following code:
var i = ProductionDbContext.GetInstance().Users.Find(1).Activitypoints;
I got a record shown in the following picture:
The record is added in the database
However, when I debug, i is always null, even though user 1 has a record in the activity points table. Am I doing something wrong or am I forgetting something?
In UserActivityPoints you have [Key] above both Type and UserId - EFCore thinks you are trying to make the two combined into your primary key. Your UserActivityPoints should look like:
[Table("activitypoints")]
public class UserActivityPoints
{
[Key]
[Column("type")]
public int Type { get; set; }
[Column("amount")]
public int Amount { get; set; }
[Key]
[Column("user_id")]
public int UserId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
}
I've removed the key, given you a column just for ID, and an object for User. Now it should work properly. Doc: https://learn.microsoft.com/en-us/ef/core/modeling/keys
Writing a model for situation where I have two tables which are customers and users. Each user record might have an optional related customer record and vice versa, but none of them is a must. I figured out that FK Associations are not what I need, but Independent Associations are. But I just can find a way to make it work, I keep getting the 'Unable to determine the principal end...The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.' exception.
My models are very simple:
public class User
{
[Key]
public int Id { get; set; }
[StringLength(20)]
public string CustomerId { get; set; }
public string Password { get; set; }
public bool Locked { get; set; }
//[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key]
[Column("Id", TypeName = "nvarchar")]
[StringLength(20)]
public string Id { get; set; } // nvarchar 20
[Required]
public string GivenName { get; set; } // nvarchar 100
[Required]
public string Surname { get; set; } // nvarchar 100
//[InverseProperty("Customer")]
public virtual User User { get; set; }
}
I've tried to add the ForeignKeyAttribute and InversePropertyAttribute, which are currently commented out, but they didn't help either. I would prefer to use data annotations and not fluent API, if it's possible in my case.
In one-to-one relation one end must be principal and second end must be dependent. Principal end is the one which will be inserted first and which can exist without the dependent one. Dependent end is the one which must be inserted after the principal because it has foreign key to the principal. When configuring one-to-one relationships, Entity Framework requires that the primary key of the dependent also be the foreign key.This problem is most easily solved by using a ForeignKey annotation on the dependent class to identify that it contains the foreign key. In your case, Customer could be the dependent and its key, Customer.UserId, should also be the foreign key. But both Keys must be declared using the same type:
public class User
{
[Key]
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public virtual User User{ get; set; }
}
I don't know how to resolve your problem using Data Annotations, but if you want to use Fluent Api, I think the configuration of the relationship would be like this:
modelBuilder.Entity<User>().HasOptional(u => u.Customer).WithOptionalPrincipal(c => c.User);
Update
I understand your escenario, but if you have the same columns that you show in your model, I think you should have a one-to-many relationship mapped in DB instead one-to-one. Try to map your relationship this way:
public class User
{
[Key]
public int Id { get; set; }
public string Password { get; set; }
public bool Locked { get; set; }
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key]
[Column("Id", TypeName = "nvarchar")]
[StringLength(20)]
public string Id { get; set; } // nvarchar 20
[Required]
public string GivenName { get; set; } // nvarchar 100
[Required]
public string Surname { get; set; } // nvarchar 100
public virtual ICollection<User> Users { get; set; }
}
Remember map your properties with the same column'names that you have in DB.
I'm using C#.NET, Entity Framework 4.1 and the code first approach. From that i have a bunch of entities and one entity is related to another entity. They have a primary key/foreign key relation.
I'm also using ViewModels to specify which attributes may be scaffolded and i was hoping that the two related entities would create a dropdown list. But that's not happening.
This is just an example of how my entity relation looks, to illustrate my problem.
UserType entity:
public class UserType
{
public int UserTypeID { get; set; }
public string Type { get; set; }
public virtual ICollection<User> Users { get; set; }
}
User entity:
public class User
{
public int UserID { get; set; }
public int UserTypeID
public string Username { get; set; }
public bool IsAdmin { get; set; }
public virtual UserType UserType { get; set; }
}
My ViewModel:
[JsonObject(MemberSerialization = MemberSerialization.OptOut, IsReference = false)]
[DataContract]
public class UserViewModel : BaseViewModel
{
[Key]
public int UserID { get; set; }
public int UserTypeID { get; set; }
[required]
public string Username { get; set; }
}
So the UserViewModel is scaffolded. And i want it to create a dropdown list for the UserType. But right now it only creates an input field for UserTypeID. How can i make it show a dropdown list with the values of UserTypes?
The scaffolder has no idea that UserTypeId in your view model relates to an actual UserType class, and therefore has no way of knwoing where to get the static list of user types from.
If you look at the example you referenced, it also includes a navigation property to the foreign key class.