Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I cast nullable DateTime as string in a LinQ query?

I am trying to take a DateTime value, and if it is not null return the Short Time String. My query looks like this: (TimeIn is NOT NULLABLE, whereas TimeOut is NULLABLE)

    var times = from t in db.TimePostings
                where t.MemberID == member.MemberID
                select new
                {
                    Date = t.TimeIn.ToShortDateString(),
                    TimeIn = t.TimeIn.ToShortTimeString(),
                    TimeOut = t.TimeOut.HasValue ? t.TimeOut.Value.ToShortTimeString() : "-------"
                };
gvTimePostings.DataSource = times;
gvTimePostings.DataBind();

but this fails when I try to databind with the error:

Could not translate expression 'Table(TimePosting).Where(t => (t.MemberID == Invoke(value(System.Func1[System.String])))).Select(t => new <>f__AnonymousType84(Date = t.TimeIn.ToShortDateString(), TimeIn = t.TimeIn.ToShortTimeString(), TimeOut = IIF(t.TimeOut.HasValue, (t.TimeOut ?? Invoke(value(System.Func`1[System.DateTime]))).ToShortTimeString(), "-------"), Hours = ""))' into SQL and could not treat it as a local expression.

I also get a similar error if I try to use:

TimeOut = t.TimeOut.HasValue ? Convert.ToDateTime(t.TimeOut).ToShortTimeString() : "-------"

however, if I change the TimeOut property to:

TimeOut = t.TimeOut.HasValue ? t.TimeOut.ToString() : "-------",

it works fine, but does not format the time like I want it (shortTimeString).

what's up with that?

like image 921
matthew_360 Avatar asked Nov 14 '11 22:11

matthew_360


4 Answers

As others have said, the problem is with trying to convert ToShortDateString etc to SQL. Fortunately, this is easy to fix: fetch the data with SQL, then format it in .NET:

var timesFromDb = from t in db.TimePostings
                  where t.MemberID == member.MemberID
                  select new { t.TimeIn, t.TimeOut };

var times = from t in timesFromDb.AsEnumerable()
            select new
            {
                Date = t.TimeIn.ToShortDateString(),
                TimeIn = t.TimeIn.ToShortTimeString(),
                TimeOut = t.TimeOut.HasValue 
                                     ? t.TimeOut.Value.ToShortTimeString() 
                                     : "-------"
            };

The call to AsEnumerable() here basically means, "stop trying to process the query using SQL; do the rest in LINQ to Objects".

like image 54
Jon Skeet Avatar answered Nov 20 '22 12:11

Jon Skeet


ToShortTimeString() has no translation in SQL. Because of that, converting the statement into a single SQL statement fails and the exception is thrown.

If you break the statement into two calls (one to retrieve the data and another to create the projection), things will work just fine:

// must call ToList to force execution of the query before projecting
var results = from t in db.TimePostings
              where t.MemberID == member.MemberID
              select new { t.TimeIn, t.TimeOut };

var times = from t in results.AsEnumerable()
            select new
            {
                Date = t.TimeIn.ToShortDateString(),
                TimeIn = t.TimeIn.ToShortTimeString(),
                TimeOut = t.TimeOut.HasValue ? 
                    t.TimeOut.Value.ToShortTimeString() :
                    "-------"
            };
like image 28
Justin Niessner Avatar answered Nov 20 '22 12:11

Justin Niessner


Have you tried:

TimeOut = t.TimeOut.HasValue ? t.TimeOut.ToString("d") : "-------",

This will normally give the short format of the DateTime. Whether it works or not will depend on whether it can be translated to SQL or not.

If it doesn't work you'll have to break the query into two parts. The first gets the data, the second format it. You'll have to convert the first query to a list (.ToList()) to force the SQL to be evaluated.

like image 25
ChrisF Avatar answered Nov 20 '22 10:11

ChrisF


Simply, it's not supported by this specific linq provider.

Your linq query is converted into an expression tree. It is up to the SQL Linq provider to convert this expression tree into SQL. Understandably, it does not have the capability to translate every single .NET function.

Your solution is to explicitly run the SQL by calling ToArray or ToList, and then allow LinqToObjects to handle the rest.

   var times = from t in db.TimePostings
            where t.MemberID == member.MemberID
           select new { 
                        TimeIn = t.TimeIn,
                        TimeOut = t.TimeOut
                      };

   var timesFormated = times.ToArray()   // Runs the query - any further processing will be run in memory by the local .NET code
                        .Select(t => new {
                                           Date = t.TimeIn.ToShortDateString(),
                                           TimeIn = t.TimeIn.ToShortTimeString(),
                                           TimeOut = t.TimeOut.HasValue ? t.TimeOut.Value.ToShortTimeString() : "-------",
                                          Hours = ""                                               
                                         }
                                );
like image 2
Andrew Shepherd Avatar answered Nov 20 '22 10:11

Andrew Shepherd