.NET Core 2.0 MVC:调用TryUpdateModelAsync前如何程序化修改字段值
Great question! I’ve run into this exact scenario building CRUD workflows with EF Core, and there are a couple of clean, straightforward ways to handle modifying fields like DateUpdated or adjusting values based on form input before executing the update with TryUpdateModelAsync. Let’s break it down with practical examples.
Core Workflow: Retrieve the Entity First, Then Modify
First, you need to fetch the existing entity from the database—TryUpdateModelAsync requires a live entity instance to apply form values to. Once you have that entity, you can tweak fields before letting the model binder update the form-specific properties.
Example 1: Setting Static Audit Fields (Like DateUpdated)
This is the most common use case—setting fields that don’t come from the user’s form input:
public async Task<IActionResult> Edit(int id) { // Step 1: Fetch the existing entity from your database var yourEntity = await _context.YourEntities.FindAsync(id); if (yourEntity == null) { return NotFound(); } // Step 2: Modify fields BEFORE calling TryUpdateModelAsync yourEntity.DateUpdated = DateTime.UtcNow; // Use UtcNow for timezone consistency yourEntity.LastUpdatedBy = User.Identity.Name; // Capture the current user // Step 3: Apply form values to the entity (use an allow-list to prevent overposting!) if (await TryUpdateModelAsync<YourEntity>( yourEntity, "", // Empty prefix since we're binding directly to the entity e => e.Name, e => e.Description, e => e.Price)) // Only allow these fields to be updated { try { await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } catch (DbUpdateException) { ModelState.AddModelError("", "Failed to save changes. Please try again."); } } // If validation fails, return the view with the existing entity data return View(yourEntity); }
Example 2: Adjusting Fields Based on Form Input
If you need to modify a field based on what the user submitted, you can either read directly from the form or use a view model for cleaner logic:
Option A: Read Directly from Request.Form
public async Task<IActionResult> Edit(int id) { var yourEntity = await _context.YourEntities.FindAsync(id); if (yourEntity == null) { return NotFound(); } // Read a form value to calculate another field's value var discountInput = Request.Form["DiscountPercentage"].ToString(); if (decimal.TryParse(discountInput, out decimal discount)) { // Calculate a discounted price based on the user's input yourEntity.DiscountedPrice = yourEntity.OriginalPrice * (1 - discount / 100); } // Set your audit field yourEntity.DateUpdated = DateTime.UtcNow; // Apply form updates to allowed fields if (await TryUpdateModelAsync<YourEntity>( yourEntity, "", e => e.Name, e => e.OriginalPrice, e => e.DiscountPercentage)) { await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } return View(yourEntity); }
Option B: Use a View Model (Cleaner for Complex Logic)
For more involved scenarios, using a view model to capture user input first makes your code more maintainable:
// Define your view model to capture user input public class YourEntityEditViewModel { public int Id { get; set; } public string Name { get; set; } public decimal OriginalPrice { get; set; } public decimal DiscountPercentage { get; set; } } // In your controller action public async Task<IActionResult> Edit(int id, YourEntityEditViewModel viewModel) { if (id != viewModel.Id) { return NotFound(); } var yourEntity = await _context.YourEntities.FindAsync(id); if (yourEntity == null) { return NotFound(); } // Adjust entity fields based on view model values yourEntity.DiscountedPrice = viewModel.OriginalPrice * (1 - viewModel.DiscountPercentage / 100); yourEntity.DateUpdated = DateTime.UtcNow; // Map view model values to the entity if (await TryUpdateModelAsync<YourEntity>( yourEntity, "", e => e.Name, e => e.OriginalPrice, e => e.DiscountPercentage)) { await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } // If validation fails, repopulate the view model with entity data viewModel.Name = yourEntity.Name; viewModel.OriginalPrice = yourEntity.OriginalPrice; return View(viewModel); }
Key Best Practices
- Always Use an Allow-List: Never skip the field list in
TryUpdateModelAsync—this prevents overposting attacks, where malicious users could update fields you didn’t intend (likeIsAdmin). - Timezone Consistency: Use
DateTime.UtcNowinstead ofDateTime.Nowfor audit fields to avoid timezone discrepancies across servers. - Validation: If modifying fields based on user input, add validation checks (e.g., ensure discount percentage is between 0 and 100) and add model errors if validation fails.
内容的提问来源于stack exchange,提问作者user6383418

