Search code examples
c#entity-frameworkasp.net-identityef-code-first-mapping

Create user and set navigation properties using ApplicationUserManager.CreateAsync


I'm using ASP.NET Identity 2.0 and Entity Framework with a Code First approach. When I create an instance of ApplicationUserManager and calll the CreateAsync method, all is fine if my users' properties are all of primitive types:

User user = new User
{
    UserName = _userManager.GetValidUserName(input.FullName),
    FullName = input.FullName,
    Email = input.EmailAddress
};

var result = await _userManager.CreateAsync(user); // OK

However, if I have a property that is an Entity type and then try to set it, the code attempts to create a new row in the relevant table for that entity and consequently crashes (because the rows are unique):

User user = new User
{
    UserName = _userManager.GetValidUserName(input.FullName),
    FullName = input.FullName,
    Email = input.EmailAddress,
    Status = _userStatusRepository.Find(us => us.Name == UserStatus.USER_STATUS_MIGRATED) // this line causes the problem
};

var result = await _userManager.CreateAsync(user); // crashes whilst trying to create a new row for USER_STATUS_MIGRATED in the DB (why would it do that?)

All I want to do is set the UserStatusId column for this row in my User table; but I can't work out how to do it. The relevant code looks like this:

public class User : IdentityUser<int, UserLogin, UserRole, UserClaim>
{
    public string FullName { get; set; }
    public virtual UserStatus Status { get; set; } // custom DB entity
    // other properties inherited from IdentityUser; e.g., UserName, Email etc.
}

public class UserStatus : Entity
{
    public static readonly string USER_STATUS_MIGRATED = "Migrated";

    public string Name { get; set; }
    public string Description { get; set; }
}

[Serializable]
public abstract class Entity
{
    public virtual int Id { get; set; }
}

public class UserEntityConfiguration : EntityTypeConfiguration<User>
{
    public UserEntityConfiguration()
    {
        ToTable("User");
        HasRequired(u => u.Status);
        Property(u => u.FullName).IsRequired();
        Ignore(u => u.PhoneNumberConfirmed);
    }
}

public class UserStatusEntityConfiguration : EntityTypeConfiguration<UserStatus>
{
    public UserStatusEntityConfiguration()
    {
        Property(e => e.Name).IsRequired().HasColumnAnnotation(IndexAnnotation.AnnotationName,
            new IndexAnnotation(new IndexAttribute("IX_Name") { IsUnique = true })
            );
        Property(e => e.Description).IsRequired();
    }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    modelBuilder.Configurations.Add(new UserEntityConfiguration());
    modelBuilder.Configurations.Add(new UserStatusEntityConfiguration());
}

Solution

  • Sounds to me as if _userStatusRepository and _userManager are using separate instances of DBContext.

    Your code should work as you expect if both are using the same context and explained helpfully here https://msdn.microsoft.com/en-us/magazine/dn166926.aspx.

    Check how and where you are creating instances of DBContext to make sure both repos have the same instance. If you are using dependency injection it is common to use a per request lifetime scope for DBContext, thus all repositories will use the same instance of the context as long as all part of the same request.