Проблема с методом POST автоматически сгенерированного контроллера для проекта Dotnet Web API

#c# #entity-framework #asp.net-core-webapi

Вопрос:

Я пытаюсь изучить веб-API Dotnet 5, создав контроллеры для моей таблицы базы данных, сгенерированной Entity framework. Структура базы данных довольно проста и содержит 3 таблицы (PaymentDetail, CustomerDetail и ClubStateDetail). Файл DbContext, созданный с помощью EF, имеет конструктор, как показано ниже :

 public partial class PaymentDetailDBContext : DbContext
    {
        public PaymentDetailDBContext()
        {
        }

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

        public virtual DbSet<ClubStateDetail> ClubStateDetails { get; set; }
        public virtual DbSet<CustomerDetail> CustomerDetails { get; set; }
        public virtual DbSet<PaymentDetail> PaymentDetails { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
                optionsBuilder.UseSqlServer("Server=.;Database=PaymentDetailDB;Trusted_Connection=True;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");

            modelBuilder.Entity<ClubStateDetail>(entity =>
            {
                entity.HasKey(e => e.ClubStateId);

                entity.ToTable("ClubStateDetail");

                entity.Property(e => e.ClubStateId)
                    .ValueGeneratedNever()
                    .HasColumnName("ClubStateID");

                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(50);
            });

            modelBuilder.Entity<CustomerDetail>(entity =>
            {
                entity.HasKey(e => e.CardOwnerId);

                entity.ToTable("CustomerDetail");

                entity.Property(e => e.CardOwnerId)
                    .ValueGeneratedNever()
                    .HasColumnName("CardOwnerID");

                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(100);

                entity.HasOne(d => d.ClubStateNavigation)
                    .WithMany(p => p.CustomerDetails)
                    .HasForeignKey(d => d.ClubState)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("FK_CustomerDetail_ClubStateDetail");
            });

            modelBuilder.Entity<PaymentDetail>(entity =>
            {
                entity.ToTable("PaymentDetail");

                entity.Property(e => e.PaymentDetailId).HasColumnName("PaymentDetailID");

                entity.Property(e => e.CardNumber).HasMaxLength(15);

                entity.Property(e => e.CardOwnerId).HasColumnName("CardOwnerID");

                entity.Property(e => e.Cvv)
                    .HasMaxLength(6)
                    .HasColumnName("CVV");

                entity.Property(e => e.Expirationdate).HasMaxLength(6);
            });

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
 

Итак, для начала, поскольку у каждого клиента должно быть состояние клуба, я создал контроллер состояния клуба, метод POST которого выглядит следующим образом :

 // POST: api/ClubStateDetails
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<ClubStateDetail>> PostClubStateDetail(ClubStateDetail clubStateDetail)
        {
            _context.ClubStateDetail.Add(clubStateDetail);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetClubStateDetail", new { id = clubStateDetail.ClubStateId }, clubStateDetail);
        }
 

Теперь я направляюсь в пользовательский интерфейс Swagger, чтобы протестировать этот API, и для этого потребовалось, чтобы данные JSON тела запроса имели формат ниже(пожалуйста, обратите внимание, что я уже ввел значения для своего идентификатора и имени clubStateId). :

 {
  "clubStateId": 1,
  "name": "Gold",
  "customerDetails": [
    {
      "cardOwnerId": 0,
      "name": "string",
      "clubState": 0
    }
  ]
}
 

Поскольку на данный момент у меня нет клиентов, я оставил эти значения без изменений, они остаются такими, какие они есть в образце данных JSON, предоставленных Swagger(я понимаю, что это может быть проблемой, но так как на данный момент у меня нет клиентов, я не уверен, что туда добавить).

Теперь, когда я пытаюсь выполнить этот почтовый запрос, я получаю сообщение об ошибке

 Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
 ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'ClubStateNavigationClubStateId'.
 

ClubStateNavigationClubStateId не является столбцом в моей таблице ClubState, однако, когда я смотрю на данные JSON, ожидаемые для контроллера CustomerDetail, я вижу это :

 {
  "cardOwnerId": 0,
  "name": "string",
  "clubState": 0,
  "clubStateNavigation": {
    "clubStateId": 0,
    "name": "string",
    "customerDetails": [
      null
    ]
  }
}
 

Поэтому мой вопрос в том, как я могу ПУБЛИКОВАТЬ данные с помощью своего API, когда у меня еще нет готовых клиентов?

Ответ №1:

Майкрософт.EntityFrameworkCore.Исключение DbUpdateException: При обновлении записей произошла ошибка. Подробности см. во внутреннем исключении. —> Microsoft.Data.SqlClient.SQLException (0x80131904): Недопустимое имя столбца «ClubStateNavigationClubStateId».

Это означает, что база данных не соответствует конфигурации EF DbContext. EF думает, что есть столбец под названием ClubStateNavigationClubStateId , которого нет в базе данных.

Вы перепроектировали модель из базы данных?

Комментарии:

1. О, я только что понял, что сделал 🙂 Я создал базу данных в SQL studio и использовал строительные леса. Итак, не могли бы вы порекомендовать подход «сначала код», чтобы избежать этого? Кроме того, зачем нам тогда строить эшафот, если возникнут эти проблемы?

2. Должен работать либо первый код, либо обратная разработка. Если есть проблема с обратным проектированием вашей модели базы данных, пожалуйста, опубликуйте повторение в своем вопросе.