Eric Longstreet
Eric Longstreet

Reputation: 893

How to save user data in .NET Core with Entity Framework

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

Answers (2)

Clint
Clint

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-Migration AddNewUser

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
}
  • Update-Database -verbose

Note: Make sure you have good separation between Domain Classes and Data Model while you are beginning.

Upvotes: 0

Chris Schaller
Chris Schaller

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

Related Questions