-3

Here i am using the below query and its taking lots of time around 14 to 15 seconds for retrieving the large amount of data. In below Query the CreatedDate is of DateTimeOffset data type.

var naId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="NA").SalesPhaseId;
var rejectedId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="Rejected").SalesPhaseId;

var data = UnitOfWork.Leads.Query().AsEnumerable()
.Where(p =>(p.SalesPhaseId == naId || p.SalesPhaseId == rejectedId) &&
p.CreatedDate.Date >= fromDate && p.CreatedDate.Date <= toDate).Select(m =>
        new
        {
        m.LeadId,
        m.LeadOwnerId,
        m.SalesPhaseId,
        m.LeadActivities,
        m.Employee,
        m.SalesPhase,
        m.CompanyName,
        m.CreatedDate,
        m.LeadHistories,
        m.LeadAddresses
        }).ToList();

I tried using the AsQueryable instead of the AsEnumerable but it gives the below error:

"The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."

Can you help me out to reduce the execution time of the query?

  • 3
    Why are you using `AsEnumerable()`? That's your `Where` clause to be executed locally. – Jon Skeet Jul 03 '14 at 06:12
  • Hey Jon, after removing the AsEnumerable() receiving the error: "The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported." So because of that using the AsEnumrable. And without .date its not filtering the data. :( – Neeraj Singh Jul 03 '14 at 06:42

3 Answers3

2

Your use of AsEnumerable is forcing the filtering to be done locally. It's pulling in all the data, then filtering it in your app. That's clearly very inefficient. Now, it seems that part of your query can't be directly expressed in LINQ to SQL. I see two options here.

Firstly you could do most of your filtering in SQL, but then do the date filtering locally:

var data = UnitOfWork.Leads.Query()
                     // Do this part of the query in SQL
                     .Where(p => p.SalesPhaseId == naId || 
                                 p.SalesPhaseId == rejectedId) 
                     .AsEnumerable()
                     // Do the rest of the query in-process
                     .Where(p => p.CreatedDate.Date >= fromDate &&
                                 p.CreatedDate.Date <= toDate)
                     .Select(...)

That's suitable if the first part will filter it down massively, and then you only need to do local processing of a small set of data.

Alternatively, you could work out what your date filtering means in terms of DateTime. It looks like you could do:

// This may not be required, depending on the source.
fromDate = fromDate.Date;
// This will be, although you may be able to get rid of the ".Date" part.
toDate = toDate.Date.AddDays(1);

var data = UnitOfWork.Leads.Query()
                     // Do this part of the query in SQL
                     .Where(p => (p.SalesPhaseId == naId || 
                                  p.SalesPhaseId == rejectedId) &&
                                 p.CreatedDate >= fromDate &&
                                 p.CreatedDate < toDate)
                     .Select(...)

That's created an equivalent query, but without using the Date property in the query itself.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Actually the CreatedDate is of DateTimeOffSet, so due to that using the CreatedDate.Date – Neeraj Singh Jul 03 '14 at 08:36
  • @NeerajSingh: Well can you just use an appropriate `DateTimeOffset` instead? (This is the sort of information which would have been useful to have when you asked the question though...) Are you actually sure about what you're trying to achieve? (Do you really want to ignore the offset part completely?) Does the suggestion at the start of my answer help you? – Jon Skeet Jul 03 '14 at 08:42
  • Sorry @Jon for not mentioning the DateTimeOffset earlier, i tried both of your suggestions. In First still taking same time like 14 to 15 sec and in Second its not giving proper filter data instead of giving 530 results its giving 522 result. – Neeraj Singh Jul 03 '14 at 08:52
  • @NeerajSingh: Well you need to look at the data in the "bad" results and thing *very* carefully about what behaviour you want. In general, I suspect that using the `Date` of a `DateTimeOffset` is a bad idea. We don't know what you're trying to achieve, which makes it harder to help. Think about data with different offsets due to different time zones, and what you actually want to filter. – Jon Skeet Jul 03 '14 at 09:03
  • In the database CreatedDate is of DateTimeOffset and in my application just using date picker for the date range and not considering the time zone as that was not required to me. Just need the data of the provided date range, just the date. Thanks for your help Jon. – Neeraj Singh Jul 03 '14 at 09:11
  • @NeerajSingh: But my point is that the reason why the filter isn't working as you expect it to may well be *due* to the time zone. You need to think really carefully about what you mean by "of the provided date range" - the date range in which time zone? If there's data which was on (say) July 1st in the time zone it was created, but that same instant in time was on July 2nd in the time zone of the person querying, should that item be included in a query for "July 2nd to July 3rd"? You can't just ignore the fact that you've got an offset - you need to think about the implications. – Jon Skeet Jul 03 '14 at 09:13
0

Everything after AsEnumerable() is executed locally rather than on the server. See also

https://stackoverflow.com/a/2013876/141172

This means that all rows in the table are returned from the database, and then filtered in your C# code.

Remove that call so that the filtering happens server-side.

EDIT

Noticed Jon's comment and it reminded me that he reimplemented LINQ to Objects as a learning exercise. His comments about the AsEnumerable() reimplementation are worth reading

I can describe its behaviour pretty easily: it returns source.

That's all it does. There's no argument validation, it doesn't create another iterator. It just returns source.

You may well be wondering what the point is... and it's all about changing the compile-time type of the expression. I'm going to take about IQueryable in another post (although probably not implement anything related to it) but hopefully you're aware that it's usually used for "out of process" queries - most commonly in databases.

Now it's not entirely uncommon to want to perform some aspects of the query in the database, and then a bit more manipulation in .NET - particularly if there are aspects you basically can't implement in LINQ to SQL (or whatever provider you're using). For example, you may want to build a particular in-memory representation which isn't really amenable to the provider's model.

https://msmvps.com/blogs/jon_skeet/archive/2011/01/14/reimplementing-linq-to-objects-part-36-asenumerable.aspx

Community
  • 1
  • 1
Eric J.
  • 147,927
  • 63
  • 340
  • 553
0

Your code should like this..

var naId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="NA").SalesPhaseId;
var rejectedId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="Rejected").SalesPhaseId;

var data = UnitOfWork.Leads.Query().AsQueryable()
.Where(p =>(p.SalesPhaseId == naId || p.SalesPhaseId == rejectedId) &&
p.CreatedDate>= fromDate.Date && p.CreatedDate <= toDate.Date).Select(m =>
    new
    {
    m.LeadId,
    m.LeadOwnerId,
    m.SalesPhaseId,
    m.LeadActivities,
    m.Employee,
    m.SalesPhase,
    m.CompanyName,
    m.CreatedDate,
    m.LeadHistories,
    m.LeadAddresses
    }).ToList();

Firstly, You need to use .ToQueryable instead of .ToIEnumerable(). Secondly, you cannot use .Date to datetime properties inside a entity framework linq query. That only works for in-memory collections like list and arrays.

Naga Sreenivas
  • 312
  • 3
  • 12
  • I see no evidence that `AsQueryable` is required here... and as `Date` isn't supported, the code you've given still won't work. – Jon Skeet Jul 03 '14 at 07:15