2

I understand AsEnumerable() is used to switch from "LINQ to SQL" to "LINQ to Object", so we can use some extra (mostly user defined) methods in our LINQ queries. But from my experience I've seen, using AsEnumerable() makes the query much slower. In that case I can enumerate the list later to apply my own methods, but still the result is pretty slow.

Can anyone suggest any better approach?

Here is some code sample of what I'm trying to do?

With AsEnumerable():

var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
                    //where r.RecordStatus == "NCF"
                    orderby r.RequisitionNo descending
                    select new PRDChemProdReq
                    {
                        RequisitionID = r.RequisitionID,
                        RequisitionNo = r.RequisitionNo,
                        RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
                        RequisitionType = DalCommon.ReturnOrderType(r.RequisitionType),
                        ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
                        RecordStatus= DalCommon.ReturnRecordStatus(r.RecordStatus),
                        RequisitionFromName = DalCommon.GetStoreName(r.RequisitionFrom),
                        RequisitionToName = DalCommon.GetStoreName(r.RequisitionTo)
                    }).ToList();

without AsEnumerable():

var Data = (from r in _context.PRD_ChemProdReq
                    //where r.RecordStatus == "NCF"
                    orderby r.RequisitionNo descending
                    select new PRDChemProdReq
                    {
                        RequisitionID = r.RequisitionID,
                        RequisitionNo = r.RequisitionNo,
                        RequisitionCategory = r.RequisitionCategory,
                        RequisitionType = (r.RequisitionType),
                        ReqRaisedOnTemp = (r.ReqRaisedOn),
                        RecordStatus= (r.RecordStatus),
                        RequisitionFrom = (r.RequisitionFrom),
                        RequisitionTo = (r.RequisitionTo)
                    }).ToList();

        foreach (var item in Data)
        {
            item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
            item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
            item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
            item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
            item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
            item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
        }
RajeshKdev
  • 6,365
  • 6
  • 58
  • 80
Badhon Jain
  • 938
  • 6
  • 20
  • 38
  • 2
    XY problem. Your real problem comes from you mixing your display logic with your data access logic. – Aron Jun 30 '15 at 04:47
  • 1
    Not much difference between the two code snippets, you are invariably bringing data in the memory, once by using the AsEnumerable, then ToList() and in other case using the ToList(). You can find the difference only when you use IEnumerable and IQueryable separately. IQueryable will help execution of query via data provider – Mrinal Kamboj Jun 30 '15 at 05:22

3 Answers3

3

It looks like you confuse these two interfaces as two totally different things. In fact IQueryable is inherited from IEnumerable, so whatever worked for you using with latter, would work with former as well, so don't need to use AsEnumerable.

Behind the scenes though these interfaces are implemented quite differently - IEnumerable will process your collection in memory, and IQueryable would pass the query to the underlying data provider. You can imagine that if a database table contains millions of records and you try to sort it, DB server can do it very quickly (using indexes) so Queryable will shine. For IEnumerable, all the data need to be loaded to your computer memory and sorted there.

For a longer answers search for "IEnumerable IQueryable difference" on SO, you will see a plenty of details:

Random Link 1 Random Link 2

Update: If you remove call .ToList from your second example, then the result won't be automatically loaded to memory. At this point you need to decide which items you want to store in memory and call your functions for them only.

var Data = (from r in _context.PRD_ChemProdReq
           orderby r.RequisitionNo descending
           select new PRDChemProdReq
           {
               // do your initialization
           });

var subsetOfData = Data.Take(100).ToList(); // Now it's loaded to memory
foreach (var item in subsetOfData)
{
    item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
    item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
    item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
    item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
    item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
    item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}

Now if you actually need to assign these properties for all your data and data can be arbitrary large, you need to work out a strategy how you can do it. The very simple option is to save them to database to a new table, then the size of processed data will be only limited by the capacity of your database.

Community
  • 1
  • 1
Alexey
  • 1,299
  • 11
  • 10
  • I can understand your points, but what should be the right way to make things faster in my case, if I don't use AsEnumerable(), I can't use user defined method call within select block, so I have to enumerate later and if I use AsEnumerable(), the whole records comes into memory, which is not good too. – Badhon Jain Jun 30 '15 at 05:27
  • @Jain The technique that is used normally is that you do a `select` of the fields you need, and then in a `foreach` you "format" them. That is what you are doing in the second example. – xanatos Jun 30 '15 at 07:05
  • @xanatos So, you are saying that is better than using .AsEnumerable(), right? I will try to follow it. Thanks. – Badhon Jain Jul 01 '15 at 03:33
1

AsEnumerable() will be slower if you add any query elements after it.

Even though AsEnumerable() doesn't execute the query directly, applying a where or orderby after the AsEnumerable() means that the Sql will get all items and then apply filtering and ordering to the collection in memory.

In short:

  • Without AsEnumerable() = filtering and ordering done in SQL
  • With AsEnumerable() and then Where or orderby = applied to whole collection brought into memory.

You can only run user defined functions on a collection in memory (as your Linq to SQL will not be able to interpret your functions to SQL code). So your second code snippet (without AsEnumerable()) is probably best.

The only other alternative is to apply your user defined functions in SQL itself.

Niels Filter
  • 4,430
  • 3
  • 28
  • 42
  • 1
    AsEnumerable() brings all the record in memory and then apply the filters, that's why we can use user defined function in select block, without using AsEnumerable(), using any user defined method call within the select block will throw an error. – Badhon Jain Jun 30 '15 at 05:25
  • 1
    @Jain, you're absolutely right, my apologies, `AsEnumerable()` doesn't execute the query bringing the result set to memory, however any further statements or filter applied will be done to the result set in memory. I've updated the answer accordingly. – Niels Filter Jun 30 '15 at 05:41
0

OK guys, I took notice of different points from all of you & came up with this:

var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
                    //where r.RecordStatus == "NCF"

                    join rf in _context.SYS_Store on (r.RequisitionFrom==null?0: r.RequisitionFrom) equals rf.StoreID into requisitionfrom
                    from rf in requisitionfrom.DefaultIfEmpty()

                    join rt in _context.SYS_Store on (r.RequisitionTo == null ? 0 : r.RequisitionTo) equals rt.StoreID into requisitionto
                    from rt in requisitionto.DefaultIfEmpty()

                    orderby r.RequisitionNo descending
                    select new PRDChemProdReq
                    {
                        RequisitionID = r.RequisitionID,
                        RequisitionNo = r.RequisitionNo,
                        RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
                        RequisitionType = r.RequisitionType == "UR" ? "Urgent" : "Normal",
                        ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
                        RecordStatus = (r.RecordStatus=="NCF"? "Not Confirmed": "Approved"),
                        RequisitionFromName = (rf==null? null: rf.StoreName),
                        RequisitionToName = (rt == null ? null : rt.StoreName)
                    });

First of all I removed my ToList() which does nothing but executes the query which is already done when I called AsEnumerable(). No points to execute the same query twice. Also my custom method calls within the select block was also playing a major part slowing down things. I tried lessen the method calls, rather used join where possible. It makes things pretty faster. Thank You all.

Badhon Jain
  • 938
  • 6
  • 20
  • 38
  • You are still mixing DAO with Display logic. ALL of your code in select should be in you UI! This is really bad software design. – Aron Jun 30 '15 at 06:30
  • @Jain, you need to ask yourself a question - do I worry about loading some crazy amount of records every time I run this query? If yes, this approach won't solve your problem anyway. If you don't worry about it, then you can go with `.AsEnumerable`. – Alexey Jun 30 '15 at 06:41
  • @Aron I know. But the project is already developed in this fashion, so I have very few choices. But definitely we will try to make things better while updating in future. – Badhon Jain Jul 01 '15 at 03:29
  • @Alexey You are right. We will have to implement server paging at some point, for the time being I'm going with .AsEnumerable. – Badhon Jain Jul 01 '15 at 03:30