tags:

views:

51

answers:

3

Hello,

I'm pretty new still at C# so I might be doing something stupid, but I've spent some time looking at this and still can't see what the problem is.

Here are some code snippets:

        double work = 0;
        ProjectRepository pr = new ProjectRepository();
        IQueryable<CalendarDetail> cds;

        // Find matching day of week
        // Then for that day, cycle through all working times

        // Return list of working times in day
        cds = pr.GetCalDetails(calendarID, startTime.DayOfWeek.GetHashCode());

        foreach (CalendarDetail cd in cds)
        {
            DateTime wts = startTime.Date + cd.WorkingTimeStart.Value.TimeOfDay;
            DateTime wtf = startTime.Date + cd.WorkingTimeFinish.Value.TimeOfDay;

            //more code here....

           if ((cds.Last().CalendarDetailID == cd.CalendarDetailID) && (finishTime > wtf))
                work += Work(startTime.Date.AddDays(1), finishTime, calendarID);
         }

The error is being thrown run-time due to my use of the cds.Last() method call. However, cds has been declared and is being used as an IQueryable object, so what is the problem?

Error text: The query operator 'Last' is not supported.

Failing a solution I'm sure I can 'logic' my way around the problem, but this seemed elegant.

Thanks,

Jonathan

+1  A: 

I don't think there's enough detail in this question to answer it, because you don't indicate what the exception actually was.

It might be worth replacing the call to .Last() to .AsEnumerable().Last(), as this would indicate whether the problem was with Last() at all, or if it was elsewhere in the query.

.AsEnumerable().Last() can be slower and can't be faster so it's not worth doing generally if you can avoid it, but (A) maybe you can't because of some limitation in the query provider and (B) the result of trying it and seeing if you get the same exception will tell you more about your problem.

Jon Hanna
It looks very much like adding the AsEnumerable() method into the chain works.Thank you very much! If someone could explain why, that would excellent.
JonRed
Just stepped through the code and it is working perfectly. Thanks again!
JonRed
`AsEnumerable` "ends" the LINQ to *whatever* query. The rest of the query (e.g., `Last`) is evaluated using LINQ to Objects. The performance problem indicated by Jon is because `.AsEnumerable().Last()` will read the entire result set (all the matching `CalendarDetails` objects) into memory and iterate it just to take the last one. It may be more efficient to create another query, reversing the ordering in `cds` and then call `First` on it; this would retrieve only a single record from the DB.
Stephen Cleary
Another alternative (if you know the number of matching records will always be small) is to call `.ToList()` after `GetCalDetails`, so the `cdr` is an in-memory list of matching records. Then it can be iterated over (and quickly access the last element) without hitting the DB again.
Stephen Cleary
Yes, that using AsEnumerable() fixes it, suggests that the query provider has some sort of difficulty with using it to build the underlying query (I don't know ProjectRepository, so I'm having to think in more general LINQ terms). If you can rewrite the query so that it does effectively the same thing (ordering and doing a Single() perhaps) then it is almost definitely going to be more performant.
Jon Hanna
+1  A: 

It's hard to guess at the cause with out knowing the exception you're thrown but you can actually do it all in linq so I thought that might be valuable information for you

var lastID = cds.Last().CalendarDetailID;
var work =  (from cd in cds
            let wts = startTime.Date + cd.WorkingTimeStart.Value.TimeOfDay
            let wtf = startTime.Date + cd.WorkingTimeFinish.Value.TimeOfDay
            where (lastID == cd.CalendarDetailID) && (finishTime > wtf)
            select Work(startTime.Date.AddDays(1), finishTime, calendarID)).Sum();

(on the assumption that work can be implicitly converted to something that .Sum() can handle. I.e. that += is not a custom operator)

Rune FS
Thanks for this. The missing code is logic about which start time to use, but this is a great example of some linq query logic.
JonRed
+1  A: 

You need to tell us what the error/exception is to help properly but the most common cause for Last() to fail is that the IQeryable provider does not support it. e.g. I don't think LinqToSQL supports Last()

Ben Robinson
It looks like Linq does not support it, and that is the problem. Adding the AsEnumerable has got me past that point in the code; I just need to ensure it is doing the right thing.
JonRed
Be aware, calling AsEnumerable() executes the expresion tree. If this is getting data from a database with 1 million rows, it will retreive all 1 million rows then discard all but the last one.
Ben Robinson
Thanks for the heads up. Maximum number of rows I'm expecting is about four, normally two.
JonRed