Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IFormFile as a Nested ViewModel Property

I'm trying to use IFormFile as a property in a nested ViewModel. I am running into issues trying to bind the ViewModel to the controller action at runtime. The AJAX request stalls and never reaches the action.

This conceptual question is in reference to my specific issue at IFormFile property in .NET Core ViewModel causing stalled AJAX Request

ViewModel:

public class ProductViewModel
{
    public ProductDTO Product { get; set; }
    public List<ProductImageViewModel> Images { get; set; }
}

Nested ViewModel:

public class ProductImageViewModel
{
    public ProductImageDTO ProductImage { get; set; }
    public IFormFile ImageFile { get; set; }
}

Action:

[HttpPost]
public IActionResult SaveProduct([FromForm]ProductViewModel model)
{
    //save code
}

I am wondering if an IFormFile Property needs to be a direct property of the ViewModel you are binding to a controller action.

The IFormFile Documentation does not seem to answer my question.

like image 361
Graham Meehan Avatar asked May 28 '19 17:05

Graham Meehan


1 Answers

The AJAX request stalls and never reaches the action.

This is a known issue that has been fixed in v3.0.0-preview and won't be merged into 2.2.x branch. See see #4802.

When posting a form with IList<Something> Something where Something has a property of IFormFile directly, it will result in an infinite loop. Because the model binding happens before the invocation of action method, you'll find that it never enters the action method. Also, if you inspect the Task Manager, you'll find that a crazy memory usage.

To walk around it, as suggested by @WahidBitar, simply create a wrapper on the IFormFile so that Something doesn't have a IFormFile directly .

As for your question itself, change your code as below:

    public class ProductViewModel
    {
        public ProductDTO Product { get; set; }
        public List<ProductImageViewModel> Images { get; set; }
    }


    public class ProductImageViewModel
    {
        public ProductImageDTO ProductImage { get; set; }
        // since this ProductImageViewModel will be embedded as List<ProductImageViewModel>
        //     make sure it has no IFormFile property directly
        public IFormFile ImageFile { get; set; }
        public IFormFileWrapper Image{ get; set; }  

        // a dummy wrapper
        public class IFormFileWrapper
        {
            public IFormFile File { get; set;}
        }
    }

Now your client side should rename the field name as below:

Images[0].ProductImage.Prop1     # Your DTO prop's
Images[0].Image.File             # instead of Images[0].ImageFile
Images[0].ProductImage.Prop2     # Your DTO prop's
Images[1].Image.File             # instead of Images[1].ImageFile
...                              # other images
Product.Prop1
Product.Prop2
...                              # other props of Product

A Working Demo :

enter image description here

like image 129
itminus Avatar answered Nov 16 '22 23:11

itminus