简体   繁体   中英

Create new entity with an existing child entity in EF Core

I have three models with one-to-many relationships (Contacts -> Accounts -> Records):

public class Contact
{
    public int Id { get; set }
    public string Name { get; set; }

    public ICollection<Account> Accounts { get; set; }
}

public class Account
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Record> Records { get; set; }
    
    public int ContactId { get; set; }
    public Contact Contact { get; set; }
}

public class Record
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int AccountId { get; set; }
    public Account Account { get; set; }
}

My method for creating a new Contact with POST request:

public async Task<bool> PostContactAsync(Contact contact)
{
    var existingAccounts = _context.Accounts.Where(a => contact.Accounts.Select(x => 
        x.Name).Contains(a.Name)).ToList();

    if (contact.Accounts.Count != existingAccounts.Count)
            throw new ArgumentException("Can`t find specified account(s) in the database");

    await _context.Contacts.AddAsync(contact);
    await _context.SaveChangesAsync();

    return true;
}

And JSON request body for creating a new Contact :

{
   "name": "Tom",
   "accounts": [
   {
      "name": "c4ece1a5-3614-4b17-93e0-d1189",
       "records": [
       {
          "name": "First record",
       },
       {
          "name": "Second record"
       }
     ]
   }
 ]
}

Account Name is a unique string field identifier. New Contact can be created if an Account with a specified name already exists in the database. The problem is that EF Core is trying to create a new Account . The challenge is to create only a new Contact and attach to existing Account new Records .

Entity Framework will be using the Id field to identify records on the database, so what's happening is you're adding a new Contact (ie an Id of zero) but the Account on that Contact also has an Id of 0, meaning Entity Framework also thinks it's new. Therefore, it adds another Account to the database with the same name (but a different id). This will also apply to the Records.

If the logic for you is that the contact is always new, you could try adding the Contact to the existing account as follows:

public async Task<bool> PostContactAsync(Contact contact)
    {
        var existingAccounts = _context.Accounts.Where(a => contact.Accounts.Select(x =>
            x.Name).Contains(a.Name)).ToList();
        
        if (contact.Accounts.Count != existingAccounts.Count)
        {
            throw new ArgumentException("Can`t find specified account(s) in the database");
        }

        foreach (var existingAccount in existingAccounts)
        {
            existingAccount.Contact = new Contact{ Id = contact.Id, Name = contact.Name};
        }

        await _context.SaveChangesAsync();

        return true;
    }

When you do this the Contact will get added to the existing account. This is far from a complete or ideal solution for you though because:

  • The previous Contact for the Account will potentially be left in the database
  • The Record entries shown in your example just get ignored
  • Any changes to the Account won't get updated in the database

If your method is just to add in a new Contact, then perhaps an alternative approach might be to pass in just the Contact record without any Account/Record child details) and have a separate parameter to specify the Account name/id it should be added to.

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