Вопрос или проблема
Я работаю над веб-приложением для телефонной книги, используя ASP.NET Core и EF Core для бэкенда.
У меня есть 3 модели: Contact
, Email
и Phone
с отношениями “один ко многим”.
Каждый контакт может иметь несколько электронных почт и телефонных номеров.
Когда я пытался добавить новый контакт через Swagger UI, я заметил, что он сохранял только данные контакта, но не электронные почты и телефонные номера. Я также пробовал вручную добавить электронную почту и телефонный номер, и после попытки в Swagger UI я снова получил данные контакта, но с пустыми массивами электронных почт и телефонных номеров…
Я размещу свой код здесь, и, надеюсь, кто-то сможет увидеть, где я допустил ошибку.
Спасибо заранее всем.
ContactController
:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using incubis_assignment.Data;
using incubis_assignment.Models;
using System.Linq.Expressions;
using System.Diagnostics.Eventing.Reader;
namespace incubis_assignment.Controllers
{
[Route("api/[controller]")]
public class ContactController : ControllerBase
{
private readonly AppDbContext _context;
public ContactController(AppDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Contact>>> GetContacts()
{
return await _context.Contacts.ToListAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<Contact>> GetOneContact(int id)
{
var contact = await _context.Contacts.FindAsync(id);
if (contact == null)
return NotFound();
return contact;
}
[HttpPost]
public async Task<ActionResult<Contact>> PostContact(Contact contact)
{
contact.Id = 0;
_context.Contacts.Add(contact);
await _context.SaveChangesAsync();
var resourceUrl = Url.Action(nameof(GetContacts), new { id = contact.Id });
return Created(resourceUrl, contact);
}
[HttpPut("{id}")]
public async Task<IActionResult> PutContact(int id, [FromBody] Contact contact)
{
if (id != contact.Id)
return BadRequest();
_context.Entry(contact).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ContactExists(id))
return NotFound();
else
throw;
}
return NoContent();
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteContact(int id)
{
var contact = await _context.Contacts.FindAsync(id);
if (contact == null)
return NotFound();
_context.Contacts.Remove(contact);
await _context.SaveChangesAsync();
return NoContent();
}
private bool ContactExists(int id)
{
return _context.Contacts.Any(e => e.Id == id);
}
}
}
AppDbContext
:
using Microsoft.EntityFrameworkCore;
using incubis_assignment.Models;
using System.Reflection.Metadata;
namespace incubis_assignment.Data;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Contact> Contacts { get; set; }
public DbSet<Email> Emails { get; set; }
public DbSet<Phone> Phones { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>()
.HasMany(c => c.Emails)
.WithOne(e => e.Contact)
.HasForeignKey(e => e.ContactId)
.IsRequired();
modelBuilder.Entity<Contact>()
.HasMany(c => c.Phones)
.WithOne(p => p.Contact)
.HasForeignKey(p => p.ContactId)
.IsRequired();
}
}
Contact
модель:
namespace incubis_assignment.Models
{
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Address { get; set; }
public ICollection<Email> Emails { get; set; } = new List<Email>();
public ICollection<Phone> Phones { get; set; } = new List<Phone>();
}
}
Email
модель:
namespace incubis_assignment.Models
{
public class Email
{
public int Id { get; set; }
public int ContactId { get; set; }
public string EmailAddress { get; set; }
public string Category { get; set; }
public Contact Contact { get; set; } // Навигационное свойство, нет необходимости в null!
}
}
Phone
модель:
namespace incubis_assignment.Models
{
public class Phone
{
public int Id { get; set; }
public int ContactId { get; set; }
public string PhoneNumber { get; set; }
public string Category { get; set; }
public Contact Contact { get; set; } // Навигационное свойство, нет необходимости в null!
}
}
Я следовал документации, но это не помогло, как и ChatGPT – только усугубило ситуацию…
Вам нужно добавить все сущности, используя разные таблицы. Ваш метод ниже добавит только в сущность Contact, так как вы указали конкретно для добавления.
[HttpPost]
public async Task<ActionResult<Contact>> PostContact(Contact contact)
{
contact.Id = 0;
_context.Contacts.Add(contact);
await _context.SaveChangesAsync();
var resourceUrl = Url.Action(nameof(GetContacts), new { id = contact.Id });
return Created(resourceUrl, contact);
}
Измените ваш метод на приведенный ниже.
[HttpPost]
public async Task<ActionResult<Contact>> PostContact(Contact contact)
{
contact.Id = 0;
_context.Contacts.Add(contact);
_context.Emails.AddRange(contact.Emails);
_context.Phones.AddRange(contact.Phones);
await _context.SaveChangesAsync();
var resourceUrl = Url.Action(nameof(GetContacts), new { id = contact.Id });
return Created(resourceUrl, contact);
}
Я использовал AddRange
, потому что существует возможность иметь несколько электронных почт и телефонов согласно вашим сущностям.
Ответ или решение
Проблема: Электронные почты и номера телефонов не добавляются в базу данных при создании нового контакта
Ваша ситуация, когда электронные почты и номера телефонов не сохраняются при добавлении нового контакта в вашем веб-приложении Phonebook на ASP.NET Core с использованием EF Core, вызывает некоторые трудности. Давайте разберемся в причине этой проблемы и решим её, шаг за шагом.
Анализ проблемы
Ключевая деталь в вашем коде заключается в том, что при выполнении метода PostContact
вы используете только метод Add
для добавления нового контакта в контекст базы данных. Это приводит к тому, что только основная сущность Contact
сохраняется, без сопутствующих Emails
и Phones
, которые являются связанными сущностями.
Метод Add
управляет только основной сущностью, и для других связанных сущностей, таких как Emails
и Phones
, необходимо явно указать, что их нужно добавить в контекст базы данных.
Решение проблемы
Используйте методы AddRange
для добавления связанных сущностей. Модифицированный код метода PostContact
может выглядеть следующим образом:
[HttpPost]
public async Task<ActionResult<Contact>> PostContact(Contact contact)
{
contact.Id = 0; // Сбрасываем ID для установки нового контакта
_context.Contacts.Add(contact); // Добавление контакта в контекст
// Добавление электронных почт и телефонов
if (contact.Emails != null && contact.Emails.Count > 0)
{
foreach (var email in contact.Emails)
{
email.ContactId = contact.Id; // Устанавливаем идентификатор контакта
}
_context.Emails.AddRange(contact.Emails); // Добавление набором
}
if (contact.Phones != null && contact.Phones.Count > 0)
{
foreach (var phone in contact.Phones)
{
phone.ContactId = contact.Id; // Устанавливаем идентификатор контакта
}
_context.Phones.AddRange(contact.Phones); // Добавление набором
}
await _context.SaveChangesAsync(); // Сохранение изменений
var resourceUrl = Url.Action(nameof(GetContacts), new { id = contact.Id });
return Created(resourceUrl, contact); // Возврат ответа
}
Объяснение изменений
-
Установление Id для связанных сущностей: Для каждой электронной почты и телефона, перед добавлением в базу данных, устанавливается свойство
ContactId
, указывающее на идентификатор созданного контакта. -
Индивидуальное добавление: После того как мы установили идентификатор контакта для всех связанных сущностей, их можно добавить в контекст базы данных с помощью метода
AddRange
. -
Проверка на
null
и пустые коллекции: Мы добавили проверки, чтобы убедиться, что не происходит попытка добавитьnull
или пустые массивы.
Заключение
Убедившись, что при добавлении контакта все сопутствующие сущности (Emails
и Phones
) корректно обрабатываются и добавляются в контекст базы данных, вы сможете избежать проблемы с отсутствием данных после вызова SaveChangesAsync()
. Это изменение поможет сохранить целостность связанных данных и правильно обновить вашу базу данных при создании новых контактов.
Если у вас возникнут дополнительные вопросы или потребуется дальнейшая помощь, не стесняйтесь обращаться!