![](/img/trans.png)
[英]Why does my ASP.NET MVC 4 application create new entities instead of updating the old ones?
[英]Why does HTTP PUT method from Angular 9 service CREATE a new DB entity instead of UPDATING the original?
我一般是 Angular SPA 和 MVC 的新手,我目前在 Visual Studio 2017 中使用 EF Core ORM(模型)和 Angular 9 前端在 MVC Core 2.2 Web 應用程序項目上工作。 該項目的目的是學習 Angular/AspNetCore MVC/EF Core 以替換使用 WCF 和 EF 6 的 .NET 4.5 Winforms 應用程序。我正在使用的源代碼示例來自同一作者的三本不同的書籍,我正在努力學習如何將所有部分組合在一起。
我的測試應用程序有一個 MVC API 控制器,它接收來自我的 Angular 服務的 HTTP 請求。 使用我的 Angular 模板,我可以從我的本地 MSSQL 數據庫中獲取一個或所有項目,並且我可以毫無問題地創建和刪除項目。 但是,當我嘗試更新現有項目時,結果是使用更新的數據創建了另一個項目,而原始項目仍然與舊數據一起存在。
當我編輯 ID 2010 並更改名稱和價格時,我得到一個新創建的國際象棋項目 - 而不是更新的 #2010。
這是我的 Angular 服務:
(other imports above)
import { Product } from "./product.model";
export const REST_URL = new InjectionToken("rest_url");
@Injectable()
export class RestDataSource {
constructor(private http: HttpClient, @Inject(REST_URL) private url: string) { }
getData(): Observable<Product[]> {
return this.sendRequest<Product[]>("GET", this.url);
}
saveProduct(product: Product): Observable<Product> {
return this.sendRequest<Product>("POST", this.url, product);
}
updateProduct(product: Product): Observable<Product> {
return this.sendRequest<Product>("PUT", this.url, product);
}
deleteProduct(id: number): Observable<Product> {
return this.sendRequest<Product>("DELETE", `${this.url}/${id}`);
}
private sendRequest<T>(verb: string, url: string, body?: Product)
: Observable<T> {
return this.http.request<T>(verb, url, {
body: body,
headers: new HttpHeaders({
"Access-Key": "<secret>",
"Application-Name": "exampleApp"
})
});
}
}
這是我的 Angular 模型功能模塊:
import { NgModule } from "@angular/core";
import { Model } from "./repository.model";
import { HttpClientModule, HttpClientJsonpModule } from "@angular/common/http";
import { RestDataSource, REST_URL } from "./rest.datasource";
@NgModule({
imports: [HttpClientModule, HttpClientJsonpModule],
providers: [Model, RestDataSource,
{ provide: REST_URL, useValue: `http://${location.hostname}:51194/api/products` }]
})
export class ModelModule { }
這是我的 Angular 9 model.repository.ts:
import { Injectable } from "@angular/core";
import { Product } from "./product.model";
import { Observable } from "rxjs";
import { RestDataSource } from "./rest.datasource";
@Injectable()
export class Model {
private products: Product[] = new Array<Product>();
private locator = (p: Product, id: number) => p.id == id;
constructor(private dataSource: RestDataSource) {
this.dataSource.getData().subscribe(data => this.products = data);
}
//removed GET, DELETE methods that are working
//this method below is supposed to CREATE (POST) if Product has no ID
//and UPDATE (PUT) if there is a Product ID, but the update is not working
saveProduct(product: Product) {
if (product.id == 0 || product.id == null) {
this.dataSource.saveProduct(product).subscribe(p => this.products.push(p));
} else {
this.dataSource.updateProduct(product).subscribe(p => {
const index = this.products.findIndex(item => this.locator(item, p.id));
this.products.splice(index, 1, p);
});
}
}
}
使用 Chrome F12 的 HTTP PUT 請求方法圖片:
這是我的 MVC API 控制器:
using Microsoft.AspNetCore.Mvc;
using Core22MvcNg9.Models;
namespace Core22MvcNg9.Controllers {
[Route("api/products")]
public class ProductValuesController : Controller {
private IWebServiceRepository repository;
public ProductValuesController(IWebServiceRepository repo)
=> repository = repo;
[HttpGet("{id}")]
public object GetProduct(int id) {
return repository.GetProduct(id) ?? NotFound();
}
[HttpGet]
public object Products() {
return repository.GetProducts();
}
[HttpPost]
public int StoreProduct([FromBody] Product product) {
return repository.StoreProduct(product);
}
[HttpPut]
public void UpdateProduct([FromBody] Product product) {
repository.UpdateProduct(product);
}
[HttpDelete("{id}")]
public void DeleteProduct(int id) {
repository.DeleteProduct(id);
}
}
}
這是 MVC 模型 Web 服務存儲庫(接口):
namespace Core22MvcNg9.Models {
public interface IWebServiceRepository {
object GetProduct(int id);
object GetProducts();
int StoreProduct(Product product);
void UpdateProduct(Product product);
void DeleteProduct(int id);
}
}
這是 MVC Web 服務模型存儲庫實現類:
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace Core22MvcNg9.Models {
public class WebServiceRepository : IWebServiceRepository {
private ApplicationDbContext context;
public WebServiceRepository(ApplicationDbContext ctx) => context = ctx;
public object GetProduct(int id)
{
return context.Products.Include(p => p.Category)
.Select(p => new {
Id = p.ProductID,
Name = p.Name,
Category = p.Category,
Price = p.Price
})
.FirstOrDefault(p => p.Id == id);
}
public object GetProducts() {
return context.Products.Include(p => p.Category)
.OrderBy(p => p.ProductID)
.Select(p => new {
Id = p.ProductID,
Name = p.Name,
Category = p.Category,
Price = p.Price
});
}
public int StoreProduct(Product product) {
context.Products.Add(product);
context.SaveChanges();
return product.ProductID;
}
public void UpdateProduct(Product product) {
context.Products.Update(product);
context.SaveChanges();
}
public void DeleteProduct(int id) {
context.Products.Remove(new Product { ProductID = id });
context.SaveChanges();
}
}
}
這是 MVC Startup.cs:
(other using statements above)
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.DependencyInjection;
using Core22MvcNg9.Models;
namespace Core22MvcNg9
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddTransient<IProductRepository, EFProductRepository>();
services.AddTransient<IWebServiceRepository, WebServiceRepository>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "exampleApp/dist";
});
}
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
SeedData.EnsurePopulated(app);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "pagination",
template: "Products/Page{productPage}",
defaults: new { Controller = "Product", action = "List" });
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "exampleApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
你能幫我嗎? 感謝您耐心閱讀長篇文章,如果您需要其他任何東西,請告訴我。
關於@derpirscher 評論的指導:
我在 API 控制器方法 UpdateProduct([FromBody] Product product) 的 MVC 代碼中設置了一個斷點。
此方法將 product.ProductID 值顯示為 0,因此該方法沒有像 [FromBody] 屬性所暗示的那樣在消息正文中找到“ProductID”。
這提醒我,Angular 數據模型使用“id”作為產品的標識,而不是 ProductID——我在 MVC 代碼和模型中更改了它,包括數據上下文。
因此,我將 MVC 模型/存儲庫和控制器中的數據上下文更改回產品標識的“Id”,使用 dotnet 遷移刪除並重建數據庫,更新現在正在運行,將 Angular 服務中的“id”匹配到使用 [From Body] 的 MVC API 控制器中的“Id”。
我的 Angular 9 html/組件仍然需要努力解決“屬性 'id' 為 null”的問題,但我很高興 MVC 更新現在正在工作。
PUT
請求不會隨機更改為POST
請求。 還有你的瀏覽器開發工具截圖顯示,該請求確實是一個PUT
請求。
錯誤可能出在服務器上。 設置斷點並檢查當您點擊PUT
端點時會發生什么以及在context.Products.Update
和context.SaveChanges
會發生什么。 也許請求正文在服務器上沒有被正確解釋,所以不是更新而是插入......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.