首页 > 解决方案 > HTTP PUT 正在创建而不是在 Angular 应用程序中更新

问题描述

我正在使用 Angular 开发一个简单的 CRUD 应用程序,当我提交更新表格元素的表单时,它会创建另一个元素而不是更新相同的元素。怎么了?

服务等级:

export class EquipmentsService {
  url = "https://localhost:5001/api/Equipment";
  constructor(private http: HttpClient) { }

   Getall(): Observable<Equipments[]>{
    return this.http.get<Equipments[]>(this.url);
  }


  GetWithId(EquipmentsID: number): Observable<Equipments>
  {
    const Url = `${this.url}/${EquipmentsID}`;
    return this.http.get<Equipments>(Url);
  }
  PostEquipment(equipment: Equipments ): Observable<any>{
    return this.http.post<Equipments>(this.url, equipment, httpOptions);
  }
  PutEquipment(equipment: Equipments): Observable<any>{
    return this.http.put<Equipments>(this.url, equipment,httpOptions);
  }

  DeleteEquipment(equipmentId: number): Observable<any>
  {
    const Url = `${this.url}/${equipmentId}`;
    return this.http.delete<Number>(Url, httpOptions);
  }
}

对话框组件:

export class DialogFormUpdateComponent implements OnInit {

  public titleForm!: string;
  formG!: FormGroup;
  equip!: Equipments;
  id!: number;


  constructor(public dialogRef: MatDialogRef<DialogFormUpdateComponent>, private fb: FormBuilder,
    private EquipmentService: EquipmentsService) {

  }

  ngOnInit(): void {

      console.log(this.id); // It's receiving Id value

      this.EquipmentService.GetWithId(this.id).subscribe(result => {
        this.titleForm = "Update Equipment";
        this.formG = this.fb.group({
          name: [result.name, [Validators.required]],
          serialNumber: [result.serialNumber, [Validators.required]],
          voltage: [result.voltage, [Validators.required]],
          electricCurrent: [result.electricCurrent, [Validators.required]],
          oil: [result.oil, [Validators.required]],
          date: [result.date, [Validators.required]],
        });
      });

  }

  public SendFormUpdate(): void {

    let newDate: moment.Moment = moment.utc(this.formG.value.date).local();
    this.formG.value.date = newDate.format("YYYY-MM-DD");
    const equipment: Equipments = this.formG.value;

    this.EquipmentService.PutEquipment(equipment).subscribe(result => {

      alert("Equipment was updated with success");
      this.formG.reset();
      this.dialogRef.close();
    })
  }

  Cancel() {

    this.dialogRef.close();
    this.formG.reset();

  }
}

设备组成:

export class EquipmentsComponent implements OnInit {
  ELEMENT_DATA!: Equipments[];
  form: any;
  titleForm!: string;
  displayedColumns: string[] = ['name', 'serialNumber', 'voltage', 'electricCurrent', 'oil', 'date', 'actions'];
  @Output() equipID: EventEmitter<number>= new EventEmitter<number>();


  public dataSource = new MatTableDataSource<Equipments>(this.ELEMENT_DATA);

  constructor(private EquipmentService: EquipmentsService, public dialog: MatDialog,
    public DialogUpate: MatDialog, public DialogComponentUpdate: DialogFormUpdateComponent) { }

  ngOnInit(): void {

    //getall on start
      this.EquipmentService.Getall().subscribe(result => {
      this.dataSource.data = result as Equipments[];
    });
  }

  //Open Create Form
  NewEquipemnt(): void {
    const dialogRef = this.dialog.open(DialogFormComponent,{
      minWidth: '300px', disableClose: true

    });

    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
      //get all
      this.EquipmentService.Getall().subscribe(result => {
        this.dataSource.data = result as Equipments[];
        });
    });
  }

  //Open Update Form
  public UpdateEquipment(EquipId:  number): void
  {

    const dialogRef = this.DialogUpate.open(DialogFormUpdateComponent,{
      minWidth: '300px', disableClose: true,
    });

    dialogRef.componentInstance.id = EquipId;
    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
        //get all
        this.EquipmentService.Getall().subscribe(result => {
        this.dataSource.data = result as Equipments[];
        });
    });

  }


}

我的更新按钮获取元素的数据将被更新,但是当我单击提交时,会在我的数据库中创建其他元素

设备控制器:

namespace TreeApi.Controllers
{
    [ApiController]
    [Route("Api/[Controller]")]
    public class EquipmentController : ControllerBase
    {
        private readonly ContextEquipment _ContextEquipment;

        public EquipmentController (ContextEquipment context)
        {
            _ContextEquipment = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Equipments>>> GetAllAsync()
        {
         return await _ContextEquipment.Equipment.ToListAsync();
        }

        [HttpGet("{EquipmentsID}")]
        public async Task<ActionResult<Equipments>> GetEquipmentAsync(int EquipmentsID)
        {
            Equipments equipment = await _ContextEquipment.Equipment.FindAsync(EquipmentsID);

            if(equipment == null)
            {
                return NotFound();
            }

            return equipment;
        }

        [HttpPost]
        public async Task<ActionResult<Equipments>> PostEquipmentAsync(Equipments equipments)
        {   
            await _ContextEquipment.Equipment.AddAsync(equipments);
            await _ContextEquipment.SaveChangesAsync();

            return Ok();
        }
        [HttpPut]
        public async Task<ActionResult> PutEquipmentAsync(Equipments equipments)
        {
            _ContextEquipment.Equipment.Update(equipments);
            await _ContextEquipment.SaveChangesAsync();

            return Ok();

        }

        [HttpDelete("{EquipmentsID}")]
        public async Task<ActionResult> DeleteEquipmentAsync(int EquipmentsID)
        {
           Equipments equipment = await _ContextEquipment.Equipment.FindAsync(EquipmentsID);
            _ContextEquipment.Remove(equipment);
            await _ContextEquipment.SaveChangesAsync();

            return Ok();
        }
    }
}

启动:

namespace TreeApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddDbContext<ContextEquipment>(options => options.UseSqlServer
                (Configuration.GetConnectionString("ConnectionDB")));
            services.AddCors();
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            app.UseHttpsRedirection();
    
            app.UseRouting();
            app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
    
            app.UseAuthorization();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

标签: angularasp.net-corehttpweb-applicationsasp.net-core-webapi

解决方案


关心

在您的SendFormUpdate方法中,您错过了id分配equipment.

对话框窗体更新组件

public SendFormUpdate(): void {

    let newDate: moment.Moment = moment.utc(this.formG.value.date).local();
    this.formG.value.date = newDate.format("YYYY-MM-DD");
    const equipment: Equipments = this.formG.value;

    this.EquipmentService.PutEquipment(equipment).subscribe(result => {

      alert("Equipment was updated with success");
      this.formG.reset();
      this.dialogRef.close();
    })
}

根据DbContext.Update(Object)方法

对于具有生成键的实体类型,如果一个实体设置了它的主键值,那么它将在修改状态下被跟踪。

如果未设置主键值,则将在已添加状态下对其进行跟踪。这有助于确保插入新实体,同时更新现有实体。

如果主键属性设置为属性类型的 CLR 默认值以外的任何值,则认为实体已设置其主键值。

因此,Equipment将创建新记录而不是更新现有记录。


解决方案

确保您需要为id(主键)赋值equipment以解决上述问题。

对话框窗体更新组件

public SendFormUpdate(): void {
    ...
    
    const equipment: Equipments = this.formG.value;
    equipment.id = this.id;

    ...
}

推荐阅读