Search code examples
c#blazorblazor-server-sideiformfile

A property of type IFormFile is always null on form submission in Blazor Server


I am working on a project where I have to update a record in which a user can also upload a picture using IFormFile everything is working fine except the IFormFile propery which always give me null on submitting the form I am sharing the code of the whole page please help me to find the issue.

@page "/Admin/UpdatePage/{Id:int}"

@using JobListingSite.Data
@using JobListingSite.Models

@inject AppDbContext db
@inject IWebHostEnvironment env


@layout AdminLayout

<div class="row justify-content-center">
    <div class="col-8">
        <h3>Create Job</h3>
    </div>

    <div  class="col-8 d-flex justify-content-center">
        <EditForm class="row g-3" method="Post" enctype="multipart/form-data" Enhance="true" Model="Job" FormName="UpdateJobForm" OnValidSubmit="Update">
            <DataAnnotationsValidator/>
            <div class="col-6">
                <label class="form-label">Job Title:</label>
                <InputText class="form-control" @bind-Value="Job.Name"></InputText>
                <InputNumber hidden class="form-control" @bind-Value="Job.Id"></InputNumber>
                <ValidationMessage For="@(()=>Job.Name)" class="text-danger"/>
            </div>

            <div class="col-6">
                <label class="form-label">Category:</label>
                <InputSelect class="form-select" @bind-Value="Job.Nature">
                    <option value="">Select job Category</option>
                    @foreach (var item in Category)
                    {
                        <option value="@item.Name">@item.Name</option>
                    }
                </InputSelect>
            </div>

            <div class="col-6">
                <label class="form-label">Salary:</label>
                <InputText class="form-control" @bind-Value="Job.salary"></InputText>
                <ValidationMessage For="@(()=>Job.salary)" class="text-danger"/>
            </div>

            <div class="col-6">
                <label class="form-label">Last Date to Apply:</label>
                <InputText class="form-control" @bind-Value="Job.LastDateToApply"></InputText>
                <ValidationMessage For="@(()=>Job.LastDateToApply)" class="text-danger"/>
            </div>

            <div class="col-12">
                <label class="form-label">Upload Photo:</label>
                <InputFile class="form-control" name="Job.Photo"/>
            </div>

            <div class="col-12">
                <button class="btn btn-success mt-3" type="submit">Update Job</button>
            </div>
        </EditForm>
    </div>
</div>
@code {

    [SupplyParameterFromForm] public Job Job { get; set; } = new();
    
    [Parameter]
    public int Id { get; set; }

    public IEnumerable<Category> Category { get; set; }
    
    protected override void OnInitialized()
    {
        Job = db.tbl_Job.Find(Id);
        Category = db.tbl_Category;
        base.OnInitialized();
    }
    
    private void Update()
    {
        if (Job.Photo == null)
        {
            db.tbl_Job.Update(Job);
            db.SaveChanges();
        }
        else
        {

            string ImageName = Job.Photo.FileName;
            string OldPhotoName = Job.Image;

            var FolderPath = Path.Combine(env.WebRootPath, "images");
            var ImagePath = Path.Combine(FolderPath, ImageName);

            var myFileStream = new FileStream(ImagePath, FileMode.Create);

            Job.Photo.CopyTo(myFileStream);

            Job.Image = ImageName;

            db.tbl_Job.Update(Job);
            db.SaveChanges();
        }
    }
}


Tried different rendering modes and different available solutions on Stack Overflow but nothing is fixing the issue.


Solution

  • I am working on a project where I have to update a record in which a user can also upload a picture using IFormFile everything is working fine except the IFormFile propery which always give me null on submitting the form

    I think you can receive the other properties value but you also can not get the updated value of these properties if you keep the default render mode.

    That is because .NET 8 blazor razor component is Static server-side rendering (static SSR) by default which cannot send the changed value if they are set with initialized value.

    For the input file value, you could add a OnChange event to set the value. To handle file uploads correctly in Blazor, you need to work with the IBrowserFile type directly, rather than trying to convert it to an IFormFile.

    Here is a whole working demo:

    @page "/Admin/UpdatePage/{Id:int}"
    @using JobListingSite.Data
    @using JobListingSite.Models    
    @inject AppDbContext db
    @inject IWebHostEnvironment env   
    @layout AdminLayout
    @rendermode InteractiveServer    //add this rendermode....
     
    
    <div class="row justify-content-center">
        <div class="col-8">
            <h3>Create Job</h3>
        </div>
    
        <div class="col-8 d-flex justify-content-center">
            <EditForm class="row g-3" method="Post" enctype="multipart/form-data" Model="Job" FormName="UpdateJobForm" OnValidSubmit="Update">
    
                <div class="col-6">
                    <label class="form-label">Job Title:</label>
                    <InputText class="form-control" @bind-Value="Job.Name"></InputText>
                    <InputNumber hidden class="form-control" @bind-Value="Job.Id"></InputNumber>
                    <ValidationMessage For="@(()=>Job.Name)" class="text-danger" />
                </div>
                <!--other properties-->
                <div class="col-12">
                    <label class="form-label">Upload Photo:</label>
                    <InputFile class="form-control" OnChange="HandleFileSelected" />
                </div>
    
                <div class="col-12">
                    <button class="btn btn-success mt-3" type="submit">Update Job</button>
                </div>
            </EditForm>
        </div>
    </div>
    @code {
       // [SupplyParameterFromForm]
        public Job Job { get; set; } = new();
        [Parameter]
        public int Id { get; set; }
    
        public IEnumerable<Category> Category { get; set; }
    
        protected override void OnInitialized()
        {
            Job = db.tbl_Job.Find(Id);
            Category = db.tbl_Category;
            base.OnInitialized();
        }
        private void HandleFileSelected(InputFileChangeEventArgs e)
        {
            var file = e.File;
            if (file != null)
            {
                // Store the file in Job.Photo
                Job.Photo = file;
            }
        }
        private async Task Update()
        {
            if (Job.Photo == null)
            {
                db.tbl_Job.Update(Job);
                db.SaveChanges();
            }
            else
            {
                string imageName = Job.Photo.Name;
                var folderPath = Path.Combine(env.WebRootPath, "images");
                var imagePath = Path.Combine(folderPath, imageName);
    
                using (var fileStream = new FileStream(imagePath, FileMode.Create))
                {
                    await Job.Photo.OpenReadStream().CopyToAsync(fileStream);
                }
                Job.Image = imageName;
                db.tbl_Job.Update(Job);
                db.SaveChanges();
            }
        }
      
    }
    

    Model

    public class Job
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Image { get; set; }
        //other properties...
        public IBrowserFile Photo { get; set; }
    }