Search code examples
c#asp.netentity-frameworkentity-framework-6asp.net-identity-2

How do I seed IdentityUser references in an Entity Framework application?


I have an Entity Framework application using ASP.NET Identity 2.2 (i.e., my context inherits from IdentityDbContext<T> and I have a User class that inherits from IdentityUser). I am successfully seeding the AspNetUsers table using the following calls in my seed method:

var testUser = new User() {
  UserName = "TestUser",
  Email = "[email protected]"
};

manager.Create(testUser, "TestPassword");

I have extended the model to include a Post class which includes a reference to my User class:

public class Post {

  public Post() {}

  [Required]
  public int Id { get; set; }

  [Required]
  public User User { get; set; }

}

And, of course, this corresponds to the following in my User class:

public class User : IdentityUser {

  public User() : base() {
    this.Posts = new HashSet<Post>();
  }

  public ICollection<Post> Posts { get; set; }

  //Additional members...
}

I am then seeding the Posts collection with the following:

 var testPost = new Post() { Id = 1, User = testUser };

 context.Posts.AddOrUpdate(
   post => post.Id,
   testPost
 );

Technically, this works fine; the Post instance is created, and the automatically generated User_Id field is correctly populated with the Id of the newly created User instance.

So what's the problem? Every time it runs, I get the following in my EntityValidationErrors: "The User field is required". It doesn't prevent my application from working, but it makes it difficult to detect legitimate errors.

Obviously, I could add custom code to my DbContext.SaveChanges() method in order to ignore this error, but I'd rather understand why it's happening in the first place, particularly if there's a problem with how I'm seeding my data.


Solution

  • It seems that when the UserManager.Create() method is called, it doesn't update the User instance with the information needed to create a reference. My assumption is that it's not populating the Id field, but I haven't confirmed.

    Regardless, the solution is to reload the user after the call to UserManager.Create(). My final code looks something like:

    var manager = new UserManager<User>(new UserStore<User>(context));
    
    var testUser = new User() {
      UserName = "TestUser",
      Email = "[email protected]"
    };
    
    if (manager.Users.Count<User>() == 0) {
      manager.Create(testUser, "TestPassword");
    }
    
    testUser = (User)manager
      .Users
      .Where<User>(u => u.UserName == "TestUser")
      .FirstOrDefault<User>();
    

    I was then able to seed the Post record the same way as before:

    var testPost = new Post() { Id = 1, User = testUser };
    
    context.Posts.AddOrUpdate(
      post => post.Id,
      testPost
    );    
    

    Note: If using a [ForeignKey] attribute, it is apparently necessary to assign the User by Id (e.g., UserId = testUser.Id) instead of by object reference (e.g., User = testUser).

    In my actual application this is all shuffled off to a CreateUser() helper method so it's easy to create multiple test users, but this covers the basics. This also addresses a flaw in my original code in that I wasn't previously checking to determine if the user had already been created or not.