简体   繁体   中英

Unable to add ApplicationUser field to more than one record in database created through EF Core

My class Subcontractor has a property ApplicationUser CreatedUser. When a subcontractor is created and added to the database I want to store the current IdentityUser as the CreatedUser. I used EF Core Migrations to create my database. The CreatedUser was mapped to CreatedUserId in the database as a foreign key by default when I added the migration.

When I run my app, the first subcontractor record that is added by a logged in user works fine and the CreatedUserId field is updated. My issue is that once a user tries to create a second subcontractor record I get an uncaught exception. If I remove updating the CreatedUser field it works fine and I can add multiple subcontractor records.

This leads me to believe that I need to add my own relationship configuration in OnModelCreating to override the default EF migration configuration? I am not sure though what I need to specify so that the Subcontractor database table will accept multiple records with the same CreatedUserId field? Or, is the default relationship correct, and do I need to change something in my create method to fix?

By default, this is what EF Core Migrations creates:

modelBuilder.Entity("SubTracker.Data.Models.Subcontractor", b =>
{
   b.HasOne("SubTracker.Data.Models.ApplicationUser", "Approver")
       .WithMany()
       .HasForeignKey("ApproverId");

   b.HasOne("SubTracker.Data.Models.ApplicationUser", "CreatedUser")
        .WithMany()
        .HasForeignKey("CreatedUserId");
});

Paired down, my class is:

 public class Subcontractor
 {
    public long Id { get; set; }
    public string Name { get; set; }
    public string AddressLineOne { get; set; }
    public string AddressLineTwo { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public ApplicationUser CreatedUser { get; set; }
}

In my controller my create method is:

private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,AddressLineOne,AddressLineTwo,City,State,Zipcode,ContactName,ContactEmail,ContactPhone")] Subcontractor subcontractor)
{
   if (ModelState.IsValid)
   {
     var user = await GetCurrentUserAsync();

     subcontractor.CreatedTimestamp = DateTime.Now;
     subcontractor.UpdatedTimestamp = subcontractor.CreatedTimestamp;
     subcontractor.AnnualSafetyPrequalApproved = false;
     subcontractor.AnnualInsuranceApproved = false;
     subcontractor.Status = "In Process";
     subcontractor.CreatedUser = user;

     _context.Add(subcontractor);

     await _context.SaveChangesAsync();

     return RedirectToAction(nameof(Index));
   }
   return View(subcontractor);
}

My ApplicationUser class:

public class ApplicationUser : IdentityUser
{
    public string Branch { get; set; }
    public string Area { get; set; }
    public string Title{ get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }        
    public string Role { get; set; }
 }

I have a separate DB context for Identity and for my application data. My paired down DBContext (removed other model classes but have nothing for ApplicationUser) for my application data is:

public class SubTrackerDbContext : DbContext
{
    public DbSet<Subcontractor> Subcontractors { get; set; }

    public SubTrackerDbContext(DbContextOptions<SubTrackerDbContext> options) : base(options)
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.QueryClientEvaluationWarning));
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Subcontractor>().ToTable("Subcontractor");        
    }
}

My DB context for Identity is:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

I think the problem is part of how you set the classes.

First you need a foreign key for the ApplicationUser' in the Subcontractor` class:

public int CreatedUserId { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }

Then (and this is optionally) your ApplicationUser must have a collection of Subcontractors :

public virtual ICollection<Subcontractor> Subcontractors { get; set; }

On your create method just specify the user id not the entire user.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM