How to post ordered items without timeout to contr

2019-08-06 11:15发布

HTML5 offline ASP.NET MVC4 application allows to enter order quantities and submit order for customer.

Product list rendered in browser contains about 4000 products. Only few products are ordered for particular order.

Using code below request times out. Browser posts all products. Request takes too much time and is probably terminated by httpruntime ExecutionTimeout

Server error log shows that all posted data is received. It looks like MVC model binder requires too much time to create product list from posted data.

How to fix this ? How to post only ordered items whose quantities are entered or other idea ? I can switch to ajax, jquery and MVC4 Web API if this is reasonable.

Controller:

public class OfflineOrderController : ControllerBase
{

    [HttpGet]
    public ActionResult Order(string customerId)
    {
        return View(new MobileOrderOrderViewModel(customerId));
    }

    [HttpPost]
    public ActionResult Order(string customerId, [Bind(Prefix = "Products")] IEnumerable<OrderedItems> Result)

    {
      ... save order to database
        return new ContentResult() { Content = "Order received" };
    }


    public AppCacheResult Manifest()
    {
        return new AppCacheResult(new[] { 
            Url.Content("~/Image/Icon/favicon")
        });
    }

}

View:

<!DOCTYPE html>
<html manifest="~/MobileOrder/Manifest">
<body>
@using (Html.BeginForm())
{
    <table>
        @for (int i = 0; i < Model.Products.Count; i++)
        {
                <tr>
                    <td>@Model.Products[i].Id</td>
                    <td>
                    @Html.HiddenFor(m => Model.Products[i].Id)
                    @Html.TextBoxFor(m => Model.Products[i].Quantity,
                    new { type = "number", min = 0 })
                    </td>
                </tr>
            }
    </table>
    <input type="submit" value="Send order">
@Html.HiddenFor(m => Model.CustomerId)
}
</body>
</html>

ViewModel:

public class MobileOrderOrderViewModel : ViewModelBase
{
    public string CustomerId { get; set; }

    public List<OrderedItems> Products { get; set; }

 public MobileOrderOrderViewModel( string customer ) {
    CustomerId = customer;
    ... populate Products property from database
    }

}

Model:

    public class OrderedItems
    {
        public string Id;
        public decimal Quantity;
    }

Update2

Using code from Imrans answer I created API controller to receive ordered products:

    public class OrderController :ApiController
    {

        public HttpResponseMessage Post(string customerid, [FromBody]List<OrderedItems> products) {
  ....
   }
}

Debugger shows that products are posted but products parameter is empty list. How to pass selected product list to Web API ?

Code used for posting is:

<script>
    $(function () {
        "use strict";
        var BASE_URL = '@Url.Content("~/")';
        $("form").submit(function (ev) {
            var elementsToSend = [];
            ev.preventDefault();
            var quantityElements = $("input.quantity").filter(function (index, element) {
                if ($(this).val() != 0) {
                    return true;
                }
                else {
                    return false;
                }
            });

            $.each(quantityElements, function (index, element) {
                var productIndex = $(element).prevAll()[1];
                var productIdElement = $(element).prevAll()[0];
                elementsToSend.push(productIndex);
                elementsToSend.push(productIdElement);
                elementsToSend.push(element);
            });
            var dataToPost = $(elementsToSend).serializeArray();
            $.post(BASE_URL + "api/Order?" + $.param({
                customerid: $("#CustomerId").val()
            }), dataToPost);
            return false;
        });
    })
</script>

2条回答
狗以群分
2楼-- · 2019-08-06 11:49

You can try AJAX to reduce the load:

$('form').submit(function () {
    var data = $(this).serializeArray().filter(function (k, v) {
        // Filter data based on order rule
    });
    // Send AJAX!
    return false;
});

Bear in mind, this only hijacks the default HTML form behavior. It can be superseded by turning off JavaScript.

EDIT: Make sure you are using the Controller / C# naming conventions. Else, binding may not work.

orderedItems.push({ Id: items[i].Id, Quantity: items[i].Quantity });
var data = JSON.stringify({ customerId: $("customerId").val(), Result: orderedItems });

There are fancier ways to do list comprehensions using filter but what you have makes sense to me.

Finally, in your Order POST action, it is a good idea to return a JsonResult.

查看更多
forever°为你锁心
3楼-- · 2019-08-06 11:53

Ok, here is what I think your view should look like

<!DOCTYPE html>
<html>
<body>
    @using (Html.BeginForm())
    {
        <table>
            @for (int i = 0; i < Model.Products.Count; i++)
            {
                <tr>
                    <td>@Model.Products[i].Id</td>
                    <td>
                        <input type="hidden" name="Products.Index" value="@i"/>
                        @Html.HiddenFor(m => Model.Products[i].Id)
                        @Html.TextBoxFor(m => Model.Products[i].Quantity, new { @class="quantity", type = "number", min = 0 })
                    </td>
                </tr>
            }
        </table>
        <input type="submit" value="Send order">
        @Html.HiddenFor(m => Model.CustomerId)
    }

    <script>
        $(function() {
            $("form").submit(function () {
                var elementsToSend = [];
                var quantityElements = $("input.quantity").filter(function(index, element) {
                    if ($(this).val() != 0) { return true; }
                    else{ return false;}
                });
                $.each(quantityElements, function (index, element) {
                    var productIndex = $(element).prevAll()[1];
                    var productIdElement = $(element).prevAll()[0];
                    elementsToSend.push(productIndex);
                    elementsToSend.push(productIdElement);
                    elementsToSend.push(element);
                });
                elementsToSend.push($("#CustomerId")[0]);
                var dataToPost = $(elementsToSend).serializeArray();
                //send elementsToSend via ajax
                $.post("/OfflineOrder/Order", dataToPost);
                return false;
            });
        })
    </script>
</body>
</html>

And here is the controller method:

[HttpPost]
public ActionResult Order(string customerId, List<OrderedItems> products)
{
    .....
}

Hope this helps.

查看更多
登录 后发表回答