Электронные адреса и номера телефонов не добавлены в базу данных при создании нового контакта.

Вопрос или проблема

Я работаю над веб-приложением для телефонной книги, используя 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); // Возврат ответа
}

Объяснение изменений

  1. Установление Id для связанных сущностей: Для каждой электронной почты и телефона, перед добавлением в базу данных, устанавливается свойство ContactId, указывающее на идентификатор созданного контакта.

  2. Индивидуальное добавление: После того как мы установили идентификатор контакта для всех связанных сущностей, их можно добавить в контекст базы данных с помощью метода AddRange.

  3. Проверка на null и пустые коллекции: Мы добавили проверки, чтобы убедиться, что не происходит попытка добавить null или пустые массивы.

Заключение

Убедившись, что при добавлении контакта все сопутствующие сущности (Emails и Phones) корректно обрабатываются и добавляются в контекст базы данных, вы сможете избежать проблемы с отсутствием данных после вызова SaveChangesAsync(). Это изменение поможет сохранить целостность связанных данных и правильно обновить вашу базу данных при создании новых контактов.

Если у вас возникнут дополнительные вопросы или потребуется дальнейшая помощь, не стесняйтесь обращаться!

Оцените материал
Добавить комментарий

Капча загружается...