Problem with Blazor InputSelect

Rusty0918 0 Reputation points
2025-03-13T19:24:19.1366667+00:00

I've been trying to learn Blazor and I've been working with a movie app tutorial that I got here: https://learn.microsoft.com/en-us/aspnet/core/blazor/tutorials/movie-database-app/?view=aspnetcore-9.0. I've added a lookup table with a "Star Rating" that is selectable through an InputSelect. My problem with this is that when I click the submit button after selecting a different Star Rating in the edit view - the selection immediately goes back to what it was before when I do it - for example, if it's 3 Stars, and I go to 4 stars, the selection goes right back to 3 stars when I click it.

NOTE: the update does seem to work. I opened up the edit page that you will see here - then manually change the value in the entry to something different from when it was loaded, then select a different value from those two, and then click the submit button and it updates / reverts back to the value that was set on the inputselect when it first loaded.

Here's the code:

@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 method="post" 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.MovieStarRating" 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; }

[SupplyParameterFromForm]

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.

// For more information, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.

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 bool MovieExists(int id)

{

using var context = DbFactory.CreateDbContext();

return context.Movie.Any(e => e.Id == id);

}

}

Blazor Training
Blazor Training
Blazor: A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.Training: Instruction to develop new skills.
23 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Ping Ni-MSFT 4,895 Reputation points Microsoft External Staff
    2025-03-14T09:11:27.34+00:00

    Hi @Rusty0918,

    Calling context.Entry(Movie).Property(m => m.MovieStarRatingId).IsModified = true; ensures EF Core marks the field as changed, forcing it to be updated.

    using var context = DbFactory.CreateDbContext();
    context.Entry(Movie).Property(m => m.MovieStarRatingId).IsModified = true;
    context.Attach(Movie!).State = EntityState.Modified;
    

    Whole working demo you could follow:

    Model

    public class Movie
    {
        public int Id { get; set; }
        public string? Title { get; set; }
        public DateOnly ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public string? Rating { get; set; }
        [DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }
        public MovieStarRating MovieStarRating { get; set; }
        public int MovieStarRatingId { get; set; }
    }
    public class MovieStarRating
    {
        public int Id { get; set; }
        public int Descr { get; set; }
        public List<Movie> Movies { get; set; }
    }
    

    Razor Component

    @rendermode InteractiveServer
    @using Microsoft.EntityFrameworkCore
    @using BlazorWebApp9_0.Models
    @inject IDbContextFactory<BlazorWebApp9_0.Data.BlazorWebApp9_0Context> 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 method="post" 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.MovieStarRating" 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 {
        [SupplyParameterFromQuery]
        private int Id { get; set; }
        [SupplyParameterFromForm]
        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();       
        }
        private async Task UpdateMovie()
        {
            using var context = DbFactory.CreateDbContext();
            context.Entry(Movie).Property(m => m.MovieStarRatingId).IsModified = true;
            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 bool MovieExists(int id)
        {
            using var context = DbFactory.CreateDbContext();
            return context.Movie.Any(e => e.Id == id);
        }
    }
    

    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Rena

    0 comments No comments

  2. Rusty0918 0 Reputation points
    2025-03-14T15:19:15.5366667+00:00

    Thank you so much! Yeah it was something simple, as I thought. I'm very new to Blazor and need to learn more the nuances of it!

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.