Dotnet Web API 项目的自动生成控制器的 POST 方法存在问题

Posted

技术标签:

【中文标题】Dotnet Web API 项目的自动生成控制器的 POST 方法存在问题【英文标题】:Issue with POST method of Auto generated Controller for Dotnet Web API project 【发布时间】:2021-12-04 19:37:49 【问题描述】:

我正在尝试通过为我的实体框架生成的数据库表模型创建一个控制器来学习 Dotnet 5 Web API。 DB 结构非常简单,包含 3 个表(PaymentDetail、CustomerDetail 和 ClubStateDetail)。通过 EF 生成的 DBContext 文件具有如下构造函数:

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);
    

因此,由于每个客户都必须拥有一个 ClubState,因此我创建了 ClubState 控制器,其 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 UI 测试此 API,它要求请求正文 JSON 数据采用以下格式(请注意,我已经为我的 clubStateId 和名称输入了值):


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

由于我目前没有任何客户,因此我将这些值保持不变,它们在 Swagger 提供的示例 JSON 数据中保持不变(我意识到,这可能是一个问题,但因为我没有任何客户到目前为止,我不知道该放什么)。

现在,当我尝试执行此 POST 请求时,我收到一条错误消息

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 表中的一列,但是当我查看 CustomerDetail 控制器预期的 JSON 数据时,我确实看到了:


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

所以我的问题是,当我还没有任何客户准备好时,如何使用我的 API 发布数据?

【问题讨论】:

【参考方案1】:

Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时出错。有关详细信息,请参阅内部异常。 ---> Microsoft.Data.SqlClient.SqlException (0x80131904): 列名“ClubStateNavigationClubStateId”无效。

这意味着数据库不匹配什么EF DbContext 配置。 EF 认为数据库中不存在名为 ClubStateNavigationClubStateId 的列。

你Reverse Engineer数据库中的模型了吗?

【讨论】:

哦,我才意识到我做到了:) 我在 SQL Studio 中创建了数据库并使用了脚手架。那么你会推荐一种代码优先的方法来避免这种情况吗?另外,如果出现这些问题,我们为什么要脚手架呢? 代码优先或逆向工程都应该有效。如果对您的数据库模型进行逆向工程时遇到问题,请在您的问题中发布重现。

以上是关于Dotnet Web API 项目的自动生成控制器的 POST 方法存在问题的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET MVC 5 控制器中使用 POST 从 dotnet Core Web API 下载文件

关于dotnet web网站的bin目录

dotNet Core初学之创建第一个dotNetCore项目

dotnet项目执行shell脚本实现简单的自动化部署

如何正确停止运行 dotnet core web 应用程序?

dotnet core docker web api没有连接