Reputation: 893
I am working with .NET Core 2.2 and I am having trouble understanding the right way to save data owned by a user.
I am using the out of the box identity scaffolding and code first migrations.
Users are stored in the AspNetUsers
table and I'm leveraging Google/Facebook providers.
As a basic example, if I have a simple model such as this, I can't find a clean example of how to reference the owner of the data in the model.
public class UserFavoriteColor
{
public int Id { get; set; }
public string Color {get; set;
public IdentityUser User { get; set; } // I've seen this in some documentation
public string UserId { get; set; } // I've seen this as well
// Other ways??
}
Additionally, there seems to be a handful of ways to retrieve and store the user information as well:
// one way to get the id
User.FindFirst(ClaimTypes.NameIdentifier).Value;
// another way
_userManager = MockUserManager.GetUserManager<ApplicationUser>();
Maybe I'm completely missing something obvious here, but I would love to see a very basic model and controller example that saves a record with the users identity value as a foreign key with a best practice approach.
Thanks!
Upvotes: 2
Views: 4894
Reputation: 6499
Remember EF uses convention over configuration and uses it to identify primary keys, foreign keys and mappings
Lets consider this scenario (See inline comments)
As per the default convention, EF makes a property as foreign key property when its name matches with the primary key property of a related entity.
public class UserFavoriteColor
{
public int Id { get; set; }
public string Color {get; set;
public int UserId { get; set; } // By default convention EF will mark this as foreign key property as its name matches the primary key of related entity
public IdentityUser User { get; set; } // Navigation property
}
public class IdentityUser
{
public int UserId { get; set; } // EF will treat this as Primary Key
public string UserName {get; set;}
public ICollection<UserFavoriteColor> FavColors { get; set; }
}
To Override Foreign: Use this data annotation against the respective property in your Domain Class
[ForeignKey("IdentityUser")]
public int ThisUserId { get; set; }
public IdentityUser User { get; set; }
[ForeignKey]
annotation on the foreign key property in the dependent entity(as in the example above)
[ForeignKey]
annotation on the navigation property in the dependent entity
[ForeignKey]
annotation on the navigation property in the principal entity
Now to add migrations and update database
Add a new user
var color1 = new UserFavoriteColor {Color="Red"}
var user = new IdentityUser {UserName="Bob",};
using (var context = new DbContext())
{
context.DBSetName.Add(user);
context.SaveChanges();
//Save changes will - Examine each object context is tracking
- Read state of object (state=new) therefore needs to Insert
- Emit SQL commands
- Execute SQL commands
- Captures any results
}
Note: Make sure you have good separation between Domain Classes and Data Model while you are beginning.
Upvotes: 0
Reputation: 16554
If in the model UserFavoriteColor
you have defined both the foreign key field UserId
and the navigation property User
then you can choose which property to set. I would use the ForeignKey
DataAnnotation attribute to further describe the relationship between the two fields.
If you only implemented the
User
navigation proerty in the model, then the Foreign Key field would still be implemented in the database level, but you would not be able to reference it in your code. So it is not explicitly required and in many online examples we leave it out to simplify the example and because many would consider exposing the foreign key in the model as implementation detail that should not be there...
public class UserFavoriteColor
{
[Key]
public int Id { get; set; }
public string Color {get; set;
[ForeignKey(nameof(UserId))]
public virtual IdentityUser User { get; set; } // Navigation Property
public string UserId { get; set; } // ForeignKey Field
}
To save a record of this type through a controller is pretty standard, generally through a controller I would set the Foreign Key field and not the Navigation Property. Its simpler code and less prone to duplicate entries being generated in the database.
If you have a User
object, you can set that instead of the UserId
and the key field will be resolved for you automatically on save changes, but the User
object MUST have been loaded from or at least attached to the same DbContext or it can result in a new User
record being created in the database.
To save a reference to the Current User then this is as good a reference as any, the mechanism that you use to retrieve the User Id is however highly dependent on how you have configured your authentication, which is out of scope for this question.
var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
var color = "Red";
...
// Insert a new favorite colour record
UserFavoriteColor record = new UserFavoriteColor {
Color = color,
UserId = currentUserId
};
context.Add(record);
context.SaveChanges();
Upvotes: 1