aspnetcore: IFormFile bind error when using in sub collection

Describe the bug

When i trying to post form with sublist of images models with IFormFile properties, the program starts to hang! see the screen

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core ‘2.2’
  2. Run this code
public class CategoryCreateModel
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public int Priority { get; set; }
        public string Abbr { get; set; }
        public string Description { get; set; }
        public string LinkName { get; set; }
        public string ParentId { get; set; }
// here error binding
        public IList<AppImageEditModel> Images { get; set; }
    }

//uploaded images sub collection
public class AppImageEditModel
    {
        public string Id { get; set; }
        public int Priority { get; set; }
        public string Name { get; set; }
        public string ImageUrl { get; set; }
// if class contains IFormFile prop, hapends binding bug
        public IFormFile AppImageFile { get; set; }
        public AppImageType ImageType { get; set; }
        public bool IsMain { get; set; }
    }

// in controller action:
        [HttpPost]
        public async Task<IActionResult> Create(CategoryCreateModel model)
        {
            if (ModelState.IsValid)
            {
                await Mediator.Send(model);
                return RedirectToAction("Index");
            }
            return View(model);
        }

No errors thrown, but frease executing: operative memory encreases from 100 megabytes to 3-4 gigabytes!!!

Screenshots when I submit form:

Increase memory screen Increase memory screen

Expected behavior

The execution is not come to action method and hangs

Binding in razor inside the form:

<form asp-action="Create" enctype="multipart/form-data"><input type="text" name="Images[0].Name" id="Images_0__Text" /> <input type="file" name="Images[0].AppImageFile" id="Images_0__AppImageFile" />

<input type="text" name="Images[1].Name" id="Images_1__Text" /> <input type="file" name="Images[1].AppImageFile" id="Images_1__AppImageFile"/>

Additional context

if you delete the property public IFormFile AppImageFile from class AppImageEditModel, the program will work well!

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 26 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Last week I encountered the same issue, but also found a work around for it. So, for people facing the same issue and who can not wait for a fix:

When you set the index as a hidden value in the form post, the dotnet Core model binding does not not fall into an infinite loop. Like this:

<input type="hidden" name="Images.Index" value="0" />
<input type="text" name="Images[0].Name" />

<input type="hidden" name="Images.Index" value="1" />
<input type="text" name="Images[1].Name" />

I have made a small reproduction of the bug and included the work around.

another solution different than @avanveelen workaround

you can just wrap the IFormFile in another class and it will work

    public class Company
    {
        public IList<Person> Employees { get; set; } = new List<Person>();
    }

    public class Person
    {
        public FormFileWrapper IdImage { get; set; }
    }

    public class FormFileWrapper
    {
        public IFormFile File { get; set; }
    }

then you don have to add the index in your html

<form action="http://localhost:5000/api/Values" method="post" enctype="multipart/form-data">
    <p><input type="file" name="Employees[0].IdImage.File"></p>
    <p><input type="file" name="Employees[1].IdImage.File"></p>
    <p><button type="submit">Submit</button></p>
</form>

@danroth27 @mkArtakMSFT @rynowak if possible, we should try to get this into Preview2 and consider it for a near-term 2.2.x patch

I would like to point out that there is no issue with previous versions of ASP.net Core. When we upgraded to ASP.NET Core ‘2.2’ we got an OutOfMemoryException due to this issue. Now we reverted to ASP.NET Core 2.1.7 waiting for a fix.

Hi everyone. Given how much pain this is causing to the customers, we’re going to reevaluate this decision. Please don’t take this as a promise that we’re going to fix this issue. Saying this is a risky area is an understatement. For now, I’m reopening the issue before we decide what the next actions will be.

Since all the answers here are a bit of a hack I thought I would add my 2 cents.

In my case I was not really able to modify my input model, so instead I instructed the c;lass with the file to [BindNever] the IFormFile property.

public class InputWithFile
{
    public bool OtherProperty { get; set; }

    [BindNever]
    public IFormFile UploadFile { get; set; }
}

Then in the wrapper class I wrote a function that manually pulls the files out of the form.

public class InputWithFileUploadList
{
    public int SomeNumber { get; set; }

    public List<InputWithFile> InputWithFiles { get; set; }

    public void HACK_FixUploadFiles(HttpContext httpContext)
    {
        if (InputWithFiles != null)
        {
            int count = 0;
            foreach (var req in InputWithFiles)
            {
                var propName = $"inputWithFileUploadList[{count++}].uploadFile";
                req.UploadFile = httpContext.Request.Form.Files.Where(i => propName.Equals(i.Name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
            }
        }
    }
}

You will have to call HACK_FixUploadFiles in your controller before using the input model. In my case this was only in 1 spot if you have multiple places you are using this input model this may be hard to maintain, but this works well if you can’t modify the structure of your input models.

You will also likely have to change the line var propName = $"inputWithFileUploadList[{count++}].uploadFile"; to whatever your file input is actually called.

I tried various permutations using custom model binders and other techniques, but I kept getting the infinite loop using any kind of model binder. This will bypass all of that completely. It can be taken out once 3.0 ships.

Last week I encountered the same issue, but also found a work around for it. So, for people facing the same issue and who can not wait for a fix:

When you set the index as a hidden value in the form post, the dotnet Core model binding does not not fall into an infinite loop. Like this:

<input type="hidden" name="Images.Index" value="0" />
<input type="text" name="Images[0].Name" />

<input type="hidden" name="Images.Index" value="1" />
<input type="text" name="Images[1].Name" />

I have made a small reproduction of the bug and included the work around.

Workaround works.

But it’s unacceptable in production. Someone can crash the app sending a valid request.