Blazor中InputSelect提交后值重置问题求助
问题解决方法
核心问题及修复步骤
1. 移除InputSelect中手动设置的selected属性
Blazor的@bind-Value会自动管理下拉框的选中状态,手动添加selected会干扰双向绑定逻辑,导致选择的新值无法正确同步到绑定属性。
原代码片段:
@if (movieStarRating.Id == Movie.MovieStarRatingId) { <option value="@movieStarRating.Id" selected>@movieStarRating.Descr</option> } else { <option value="@movieStarRating.Id">@movieStarRating.Descr</option> }
修改后:
<option value="@movieStarRating.Id">@movieStarRating.Descr</option>
2. 移除Movie属性上的[SupplyParameterFromForm]特性
在交互式服务器组件中,该特性会在表单提交时尝试从表单数据重新绑定Movie对象,覆盖你在组件中修改后的实例,直接导致值被重置回初始状态。
原代码:
[SupplyParameterFromForm] private Movie? Movie { get; set; }
修改后:
private Movie? Movie { get; set; }
3. 修正ValidationMessage的绑定目标
当前ValidationMessage指向Movie.MovieStarRating,但实际绑定的是MovieStarRatingId,需要调整表达式指向正确的属性:
原代码:
<ValidationMessage For="() => Movie.MovieStarRating" class="text-danger"></ValidationMessage>
修改后:
<ValidationMessage For="() => Movie.MovieStarRatingId" class="text-danger"></ValidationMessage>
4. 优化EF Core的更新逻辑(可选但推荐)
直接使用Attach并设置EntityState.Modified可能引发并发问题,建议先从数据库获取最新实体,再更新需要修改的属性,同时避免过度提交风险:
原UpdateMovie方法:
private async Task UpdateMovie() { using var context = DbFactory.CreateDbContext(); context.Attach(Movie!).State = EntityState.Modified; try { await context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!MovieExists(Movie!.Id)) { NavigationManager.NavigateTo("notfound"); } else { throw; } } NavigationManager.NavigateTo("/movies"); }
修改后:
private async Task UpdateMovie() { using var context = DbFactory.CreateDbContext(); var existingMovie = await context.Movie.FirstOrDefaultAsync(m => m.Id == Movie!.Id); if (existingMovie == null) { NavigationManager.NavigateTo("notfound"); return; } // 手动更新需要修改的属性,避免过度提交 existingMovie.Title = Movie.Title; existingMovie.ReleaseDate = Movie.ReleaseDate; existingMovie.Genre = Movie.Genre; existingMovie.Price = Movie.Price; existingMovie.Rating = Movie.Rating; existingMovie.MovieStarRatingId = Movie.MovieStarRatingId; try { await context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!MovieExists(Movie.Id)) { NavigationManager.NavigateTo("notfound"); } else { throw; } } NavigationManager.NavigateTo("/movies"); }
完整修正后的代码
@page "/movies/edit" @rendermode InteractiveServer @using Microsoft.EntityFrameworkCore @using BlazorWebAppMovies.Models @using BlazorWebAppMovies.Data @inject IDbContextFactory<BlazorWebAppMovies.Data.BlazorWebAppMoviesContext> DbFactory @inject NavigationManager NavigationManager <PageTitle>Edit</PageTitle> <h1>Edit</h1> <h2>Movie</h2> <hr /> @if (Movie is null) { <p><em>Loading...</em></p> } else { <div class="row"> <div class="col-md-4"> <EditForm Model="Movie" OnValidSubmit="UpdateMovie" FormName="edit" Enhance> <DataAnnotationsValidator /> <ValidationSummary role="alert"/> <input type="hidden" name="Movie.Id" value="@Movie.Id" /> <div class="mb-3"> <label for="title" class="form-label">Title:</label> <InputText id="title" @bind-Value="Movie.Title" class="form-control" /> <ValidationMessage For="() => Movie.Title" class="text-danger" /> </div> <div class="mb-3"> <label for="releasedate" class="form-label">Release Date:</label> <InputDate id="releasedate" @bind-Value="Movie.ReleaseDate" class="form-control" /> <ValidationMessage For="() => Movie.ReleaseDate" class="text-danger" /> </div> <div class="mb-3"> <label for="genre" class="form-label">Genre:</label> <InputText id="genre" @bind-Value="Movie.Genre" class="form-control" /> <ValidationMessage For="() => Movie.Genre" class="text-danger" /> </div> <div class="mb-3"> <label for="price" class="form-label">Price:</label> <InputNumber id="price" @bind-Value="Movie.Price" class="form-control" /> <ValidationMessage For="() => Movie.Price" class="text-danger" /> </div> <div class="mb-3"> <label for="rating" class="form-label">Rating:</label> <InputText id="rating" @bind-Value="Movie.Rating" class="form-control" /> <ValidationMessage For="() => Movie.Rating" class="text-danger" /> </div> <div class="mb-3"> <label for="moviestarrating" class="form-label">Movie Star Rating:</label> <InputSelect id="moviestarrating" @bind-Value="Movie.MovieStarRatingId" class="form-control"> @foreach (var movieStarRating in StarRatings) { @if (movieStarRating.Id != 0) { <option value="@movieStarRating.Id">@movieStarRating.Descr</option> } } </InputSelect> <ValidationMessage For="() => Movie.MovieStarRatingId" class="text-danger"></ValidationMessage> </div> <button type="submit" class="btn btn-primary">Save</button> </EditForm> </div> </div> } <div> <a href="/movies">Back to List</a> </div> @code { private BlazorWebAppMoviesContext context = default!; [SupplyParameterFromQuery] private int Id { get; set; } private Movie? Movie { get; set; } private IList<MovieStarRating> StarRatings { get; set; } = []; protected override async Task OnInitializedAsync() { using var context = DbFactory.CreateDbContext(); Movie ??= await context.Movie.FirstOrDefaultAsync(m => m.Id == Id); if (Movie is null) { NavigationManager.NavigateTo("notfound"); } StarRatings = await context.MovieStarRating.ToListAsync(); } // To protect from overposting attacks, enable the specific properties you want to bind to. private async Task UpdateMovie() { using var context = DbFactory.CreateDbContext(); var existingMovie = await context.Movie.FirstOrDefaultAsync(m => m.Id == Movie!.Id); if (existingMovie == null) { NavigationManager.NavigateTo("notfound"); return; } // 更新指定属性,避免过度提交 existingMovie.Title = Movie.Title; existingMovie.ReleaseDate = Movie.ReleaseDate; existingMovie.Genre = Movie.Genre; existingMovie.Price = Movie.Price; existingMovie.Rating = Movie.Rating; existingMovie.MovieStarRatingId = Movie.MovieStarRatingId; try { await context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!MovieExists(Movie.Id)) { NavigationManager.NavigateTo("notfound"); } else { throw; } } NavigationManager.NavigateTo("/movies"); } private bool MovieExists(int id) { using var context = DbFactory.CreateDbContext(); return context.Movie.Any(e => e.Id == id); } }
内容的提问来源于stack exchange,提问作者Rusty0918-B
相关产品推荐
相关产品推荐

