Send a List<T> with multiple items to the controller

Asked

Viewed 2,378 times

1

Well, I’m facing the following problem in a project I’m working on: how to pass a list (List) with approx. 500~1000 View lines for the Controller?

Actually, this list of mine has a field called "Selected (bool)" from which the user selects only the lines he wants to generate an action, which gives around 50, 100, or even sometimes all of them.

I’m building this into a table and performing a "for" to popular it. The response time to build the View is excellent, but to send the list to my controller and start validating the selected lines and then write to the database, it is absurdly slow/stuck. My controller is receiving the List as a parameter and performing the actions after receipt.

My Controller:

public ActionResult Create(List<MyClass> list) {
    foreach (var item in list) {
        if (item.Checked) {
            // realiza ações
        }
    }
}

My View:

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    @* Aqui fica todo o cabeçalho (th) e componentes HTML *@

    @for (int r = 0; r < Model.Count(); r++)
    {
        <tr class="tr-clickable">
            @Html.HiddenFor(i => i[r].ID_Usuario)
            <td>@Html.CheckBoxFor(i => i[r].Checked)</td>
            <td>@Html.DisplayFor(i => i[r].Matricula)</td>
            <td>@Html.DisplayFor(i => i[r].Nome)</td>
            <td>@Html.DisplayFor(i => i[r].Value)</td>
        </tr>
    }

    @* Aqui ficam os componentes HTML restantes *@

    <div class="control-group margin-top-20 pull-right">
        <div class="controls">
            <input type="submit" class="btn btn-default" value="Gerar" />
        </div>
    </div>
}

Is there any smarter way to make that "pass"?

I thought of doing via ajax-jQuery, passing line-by-line and recording one-by-one. It works. However, the user has to have the power to give Submit only when he is sure of the selected lines...

  • 3

    Post as your controller.

  • @Randrade edited!

  • I believe that the Model Binder was not made to work with so many records in a single request. I think it would be better if you simplify the modeling, making the Action receive a dictionary.

  • @Ciganomorrisonmendez Dictionary? I don’t think I understand it very well... An object like "Dictionary"? ^~

  • @Antoniofilho That’s right. Can you please put your View in the question?

  • @Gypsy omorrisonmendez Edited!

Show 1 more comment

3 answers

4

If you want to pass the list to the View to change only in submit, I think the best option would be for you to do the foreach() only with the selected items, this way:

public ActionResult Create(List<MyClass> list) {
    foreach (var item in list.Where(l => l.Selected)) {

            // realiza ações

    }
}

This way you will only go through the list of those who are selected.

  • @Antoniofilho make a test and let me know. In case it is still taking a while, we can see a way to send only the selected ones to the controller.

  • Yes, so far so good. The problem is that the list does not even reach my controller. When there are about 200 items in this list and I give a form Ubmit, it doesn’t even load the controller, it’s kind of stuck...

2

The most interesting thing would be to know how your Controller and View. But I’ll try to show you through a practical example:

Let’s imagine that you need the user to select only the products he wants to buy and send to the server. However, you just need to know what the Id of each product selected to make the actual purchase.

The class Productwould look like this:

namespace AntonioFilho.Models
{
    public class Product
    {
        public int Id {get; set;}
        public string Name {get; set;}
        public decimal Price {get; set;}
    }
}

The Controller would look something like this:

namespace AntorioFilho.Controllers
{
    public class ProductController : Controller
    {
        // Camada fictícia apenas para ilustração ;)
        private IProductRepository _repository = new ProductRepository();

        public ActionResult Index()
        {
             return View();
        }
        public ActionResult Buy()
        {
            IList<Product> products = _repository.GetAllProducts();
            return View(products);
        }

        [HttpPost, ValidateAntiForgeryToken]
        public ActionResult Buy(List<Product> products)
        {
            bool ok = _repository.SelectProducts(products);
            return RedirectToAction("Index");
        }
    }
}

Already his View would be so:

@model IList<Product>

@using (Html.BeginForm("Buy", "Product", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <table>
        <tr>
            <th><input type="checkbox" id="checkall" /></th>
            <th>Product name</th>
        </tr>
    @for(int i = 0;i < Model.Length;i++)
    {
        <tr>
            <td>
                @Html.HiddenFor(m => Model[i].Price, new { @disabled = "disabled", @id = String.Format("product{0}", i) })
                @Html.CheckboxFor(m => Model[i].Id, new { @class = "check", @idFake = String.Format("product{0}", i) })
            </td>
            <td>@Model[i].Name</td>
        </tr>
    }
        <tr>
            <td colspan="2">
                <input type="submit" value="comprar" />
            </td>
        </tr>
    </table>
}

<script>
    $(document).ready(function() {
        $('.check').click(function(){
            var id = $(this).attr('idFake');
            if(this.checked)
                $('#' + id).removeAttr('disabled');
            else
                $('#' + id).attr('disabled', 'disabled');
        });
    });
</script>

I hope I helped the/

  • @Thanks for the help. So, basically that’s what I do today, but I pass a List<T>. What happens is that I have to pass 2 attributes, the ID attribute and the VALUE attribute (because it is a momentary value, which can be changed in a future).

  • @Antonio son, then you can add Javascript and change the Controller to deal with this case. I made an issue in the reply.

  • That way, you can send only what you need.

  • @Uliquemessias But how will I receive the ID fields and the Value field? The JS you entered it only moves the component itself..

  • @Antoniofilho, all of you <input type="algumacoisa" /> HTML that are inside a <form></form> will be sent to the server. In the case of <input type="checkbox" />, it will only be sent if it is marked. So, when you mark it, it will enable the other "brother" control that is hidden (<input type="hidden" />) and when the user clicks Submit, will send only the Productthose that are marked.

  • @Antoniofilho, if my or another answer solved your problem, you could mark as "answer"?

  • @Uliquemessias is that actually I haven’t had a "plausible" answer yet. Your answer, for example, works. But if I have a table with 1000 rows and select 500, it will still take time to make Httppost understand...

  • @Antonio son, I understand. But that way, you won’t find a pausible response for so long, unless this "long delay" is something over three seconds. In this case, you would have to review the structure of your application (see if there are any Handler in his pipeline making unnecessary operations to each requisição) or even your internet connection (in case of testing with an external server) or your computer’s settings (if it is too slow).

  • If the problem is in the pipeline. I advise looking at the Global.asax.cs or in the archives Startup.cs. It may be that some script is running unnecessarily for each request and it takes your application longer to respond.

Show 4 more comments

1


I solved this problem using jQuery-Ajax:

   $('input[type="button"][data-submit]').click(function () {

        if (confirm('Você confirma?')) {

            var array = [];

            $('#table > tbody > tr').each(function () {
                var $tr = $(this);
                // aqui é só colocar algo que filtre apenas as tr's com um checkbox igual a true (':checked')
                var usid = $tr.find('[data-usid]').val();
                var irvlr = $tr.find('[data-irvlr]').val();
                array.push({
                    User: usid,
                    Value: irvlr
                });
            });

            if (array.length > 0) {
                $.ajax({
                    contentType: 'application/json; charset=utf-8',
                    dataType: 'json',
                    type: 'POST',
                    url: '@Url.Action("MyAction", "MyController")',
                    data: JSON.stringify({ 'list': array }),
                    success: function (result) {
                        window.location.href = result.url;
                    },
                    error: function (ex) {
                    }
                });
            }
            else {
                alert('No items selected.');
            }

        }

    });

Thank you all!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.