1

I am building an application using asp.net MVC 5 and have a grid working with IPagedList.MVC version 4.5.0.0, AutoMapper and Entity Framework.

In the project I have a BusinessLayer which is what my Action talks to, as I don't want the Action method to talk to Entity Framework directly. So my BLL has the following method:

        public IPagedList<ActiveContractViewModel> GetAllContracts(string regNumFilter, int page)
    {
        var lstcontractViewModel = new List<ActiveContractViewModel>();
        using (ActiveContractRepository activeContractRepos = new ActiveContractRepository(new UnitOfWork()))
        {
            var activeContractList = activeContractRepos.All.OrderByDescending(x => x.Id).Include(c => c.Contractor);

            if (regNumFilter.Trim().Length > 0)
            {
                activeContractList = activeContractRepos.All.Where(x => x.RegistrationNumber.Contains(regNumFilter)).OrderByDescending(x => x.Id).Include(c => c.Contractor);
            }

            foreach (var activeContract in activeContractList)
            {
                Mapper.CreateMap<DomainClasses.ActiveContract, ActiveContractViewModel>().ForMember(dest => dest.ContractorModel, opts => opts.MapFrom(src => new ContractorViewModel
                    {
                        Id = src.Contractor.Id,
                        Name = src.Contractor.Name,
                        ContactPerson = src.Contractor.ContactPerson,
                        Phone = src.Contractor.Phone,
                        Fax = src.Contractor.Fax,
                        Address = src.Contractor.Address,
                        VendorNumber = src.Contractor.VendorNumber,
                        FederalTaxId = src.Contractor.FederalTaxId
                    }
                ));

                Mapper.AssertConfigurationIsValid();
                lstcontractViewModel.Add(Mapper.Map<ActiveContractViewModel>(activeContract));
            }
        }

        return lstcontractViewModel.ToPagedList(page, 20);
    }

I'm mapping my ActiveContract class (from Entity Framework) to a model (ActiveContractVieWModel) it works fine, data is returned and paging works. But I noticed while debugging that the foreach loop would also go through all records, if I have 2500 records it loops through all building a large list, which is then use on the ToPageList method.

Is there a better way to get around this, so i can build my model and fill it with just the 20 records I need and have the IPagedList know the total size?

Paritosh
  • 4,243
  • 7
  • 47
  • 80
  • First, I would take that `CreateMap` out of the for loop; it only needs to be performed once at program startup. See http://stackoverflow.com/questions/6825244/where-to-place-automapper-createmaps. – Jasen Apr 23 '14 at 21:01

4 Answers4

3

I ended up looking into IPageList.MVC some more and saw the author had posted about this: https://github.com/troygoode/pagedlist#example-2-manual-paging

"In some cases you do not have access something capable of creating an IQueryable, such as when using .Net's built-in MembershipProvider's GetAllUsers method. This method offers paging, but not via IQueryable. Luckily PagedList still has your back (note the use of StaticPagedList):"

I switched to using the StaticPagedList and it works better now, just grabbing the number of records I want and paging works as well.

Paritosh
  • 4,243
  • 7
  • 47
  • 80
1

Thats because you are retrieving all items. In your LINQ queries, you are only filtering by regNumFilter

var activeContractList = activeContractRepos
    .All
    .OrderByDescending(x => x.Id)
    .Include(c => c.Contractor);

activeContractList = activeContractRepos
    .All
    .Where(x => x.RegistrationNumber.Contains(regNumFilter))
    .OrderByDescending(x => x.Id)
    .Include(c => c.Contractor);

To retrieve a specific number of rows(In you case 20 items per page), use Skip() and Take() before you iterate the results.

Sample code:

var activeContractList = activeContractList 
    .Skip(20 * page)
    .Take(20);

foreach (var activeContract in activeContractList)
{
    ....
}
Yorro
  • 11,445
  • 4
  • 37
  • 47
  • This will return 20 results sure, but the pagination will only show the number of pages = 1. That is what the OP is asking that when 20 results are retrieved, how to set the total number of records on the PagedList so that the number of pages shown to the user reflects total number of records in the database. This doesn't answer the question, it just extends it! – Syed Waqas Nov 07 '17 at 10:41
0

I m not using automapper but valueinjecter.

What i do is "mapping" IList<TModel> to IList<TViewModel> then you wont have the code. it will be more clean.

PagedList is not aware of TEntity, it just does paging. Dont forget paging is lazy.

With Valueinjecter I have following code:

https://github.com/fatagun/NetCollab/blob/master/NetCollab.Web/Mappers/Mapper.cs

DarthVader
  • 52,984
  • 76
  • 209
  • 300
0

I think what you need is AutoMapper Queryable-Extensions.

When ever you use the IEnumerable<T> interface the underline Entity query is materialized. So the query will be run against your whole table well before ToPagedList "takes" and "skips" some pages. Instead you will have to go with the IQueryable<T> variations.

When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map functions, you may notice that the ORM will query all the fields of all the objects within a graph when AutoMapper is attempting to map the results to a destination type

cleftheris
  • 4,626
  • 38
  • 55