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

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?

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".

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() :
"-------"
};

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.

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 = ""
}
);

Your query is transformed by LINQ to an SQL that is fired against your database, and there is obviously no way to translate t.TimeOut.Value.ToShortTimeString() to SQL.
Possible solutions are:
First fetch your data from database (by calling .ToList() or .ToArray() on your LINQ query), that converts your IQueryable<> into IEnumerable<> and then apply your transformation for every row fetched.
Use a view that takes the original table and performs the conversion using CONVERT() function on the SQL Server and use it as the source for your Linq-to-SQL class. That would be performanter, but requires some server-side changes.

I had the same problem in a project in vb.net.
The solution I've found is based on the use of:
if(table.field.hasvalue, table.field.value.ToShortDateString, string.format("NULL"))
In this case, if the selected field (table.field) has a value this is converted into a date string otherwise if the field hasn't a value the output field is filled with string "NULL"

Related

How to convert a string into a datetime in Linq to Entities query?

My Linq to entities query is written as below.
The datatype of DATECOLUMN1 in my ORACLE database is of string.
Datetime FilterStartDate = DateTime.Now;
var query = from c in db.TABLE1
join l in db.TABLE2 on c.FK equals l.PK
where (FilterStartDate >= DateTime.ParseExact(l.DATECOLUMN1, "dd/MM/yyyy", CultureInfo.InvariantCulture) : false) == true
select c;
Writing above query gives me an error of not supported. How can I convert DATECOLUMN1 into a datetime to compare it.
P.S. I do not have control over database schema, so changing datatype of column in Oracle database is not a feasible solution for me.
In you Model, add the following property to your partial class TABLE2:
public DateTime DATECOLUMN1_NEW
{
get
{
return DateTime.ParseExact(DATECOLUMN1, "dd/MM/yyyy", CultureInfo.InvariantCulture);
}
set { }
}
Then, in you LINQ query, use DATECOLUMN1_NEW (it's already in DateTime format) in place of DATECOLUMN1.
Erm.. I think the problem you are having is that you are putting ": false" in there.
It looks like you are trying to use a condtional operator (?:) but you forgot the "?".
I don't think you actually need this as you are just trying to determine if the date is greater or not. Also if ParseExact fails it will throw an exception (not what you want) so you should use TryParse instead and handle the true/false returned and the out value to determine whether or not the date is (a) Actually a date (b) less then FilterStartDate.
You can use two alternatives:
Use the function described in the answer here: How to I use TryParse in a linq query of xml data?
Use the following fluent syntax version which I think is more readable.
var query = db.Table1.Join(db.Table2, x => x.FK, y => y.PK, (x, y) => x).Where(x =>
{
DateTime Result;
DateTime.TryParse(x.Date, out Result);
return DateTime.TryParse(x.Date, out Result) && FilterStartDate >= Result;
});

Printing Month Name in Linq

Is there any Inbuilt function in Linq to Print the month Name while working with LINQPAD?
I want to print the month name in the following Scenario
var query = from e in Employees
let month=e.BirthDate.GetValueOrDefault()
let birthmonth=month.ToString("MMMM")
select birthmonth;
query.Dump();
When I run this it is throwing NotSupportedException.
how to print the month name in Linq to Sql?
Rather than using ToString, try string.Format. Something like:
var query = (from e in Employees
let month = e.BirthDate.GetValueOrDefault()
let birthmonth = string.Format("{0:MMMM}", month)
select birthmonth);
query.Dump();
This seems to work from my local testing, although it is not included as part of the SQL query.
Do it in two steps, one to get the months from the database, then another using Linq-To-Objects to perform the formatting.
var birthDates = Employees.Select(e => e.BirthDate).ToList();
var query = birthDates.Select(d => d != null ? d.ToString("MMMM") : "Null");
query.Dump();
Whatever ORM you are using can't convert the string formatting part of you query into SQL that works on your database. So, doing it in two steps and using ToList to evaluate inbetween overcomes that problem.

Read from CellSet using LINQ

AdomdCommand cmd = new AdomdCommand(commandText, conn);
CellSet cs = cmd.ExecuteCellSet();
var result = from a in cs
select new
{...};
Is it possible to use LINQ to read from CellSet? I have tried using DataTable instead of CellSet but it's much slower (Example: I took a query that using DataTable takes ~45 sec to execute, but using CellSet it takes ~5 sec).
The error I get when trying to use LINQ:
Could not find an implementation of the query pattern for source type
'Microsoft.AnalysisServices.AdomdClient.CellSet'. 'Select' not found.
UPDATE:
I have tried Enrico's suggestion and so far it doesn't return any errors. Next problem is how to read a value from a cell. Here's what I have tried so far:
var result = from cell in cs.Cells.Cast<Cell>()
select new searchResultsPolicy
{
PolicyNumber = ...
InsuredName = cell.Field<string>("[Insured].[DBA Name].[DBA Name].[MEMBER_CAPTION]"),
Agency = cell.Field<string>("[Agency].[Agency Name].[Agency Name].[MEMBER_CAPTION]"),
Market = cell.Field<string>("[Market].[Market Name].[Market Name].[MEMBER_CAPTION]"),
Revenue = String.Format("{0:#,##0}", cell.Field<double?>("[Measures].[Revenue]") ?? 0),
Premium = String.Format("{0:#,##0}", cell.Field<double?>("[Measures].[Premium]") ?? 0)
};
The reason you get that error is that the CellSet class in itself doesn't implement IEnumerable<T>, which is required by LINQ.
Try executing the LINQ query over the CellSet.Cells property instead. That will return a CellCollection object, which implements IEnumerable. From there you can easily convert it to an IEnumerable<T> by using the Enumerable.Cast<T> method.
AdomdCommand cmd = new AdomdCommand(commandText, conn);
CellSet cs = cmd.ExecuteCellSet();
var result = from cell in cs.Cells.Cast<Cell>()
select new
{ ... };
See also:
Retrieving Data Using the CellSet

What is the correct way of reading single line of data by using Linq to SQL?

I'm very new to Linq, I can find multi-line data reading examples everywhere (by using foreach()), but what is the correct way of reading a single line of data? Like a classic Product Detail page.
Below is what I tried:
var q = from c in db.Products
where c.ProductId == ProductId
select new { c.ProductName, c.ProductDescription, c.ProductPrice, c.ProductDate };
string strProductName = q.First().ProductName.ToString();
string strProductDescription = q.First().ProductDescription.ToString();
string strProductPrice = q.First().ProductPrice.ToString();
string strProductDate = q.First().ProductDate.ToString();
The code looks good to me, but when I see the actual SQL expressions generated by using SQL Profiler, it makes me scared! The program executed four Sql expressions and they are exactly the same!
Because I'm reading four columns from a single line. I think I must did something wrong, so I was wondering what is the right way of doing this?
Thanks!
Using the First() extension method would throw the System.InvalidOperationException when no element in a sequence satisfies a specified condition.
If you use the FirstOrDefault() extension method, you can test against the returned object to see if it's null or not.
FirstOrDefault returns the first element of a sequence, or a default value if the sequence contains no elements; in this case the default value of a Product should be null. Attempting to access the properties on this null object will throw ArgumentNullException
var q = (from c in db.Products
where c.ProductId == ProductId
select new { c.ProductName, c.ProductDescription, c.ProductPrice, c.ProductDate }).FirstOrDefault();
if (q != null)
{
string strProductName = q.ProductName;
string strProductDescription = q.ProductDescription;
string strProductPrice = q.ProductPrice;
string strProductDate = q.ProductDate;
}
Also, you shouldn't have to cast each Property ToString() if you're object model is setup correctly. ProductName, ProductDescription, etc.. should already be a string.
The reason you're getting 4 separate sql queries, is because each time you call q.First().<PropertyHere> linq is generating a new Query.
var q = (from c in db.Products
where c.ProductId == ProductId
select new { c.ProductName, c.ProductDescription, c.ProductPrice, c.ProductDate }
).First ();
string strProductName = q.ProductName.ToString();
string strProductDescription = q.ProductDescription.ToString();
string strProductPrice = q.ProductPrice.ToString();
string strProductDate = q.ProductDate.ToString();

Linq Error: InvalidOperationException: Could not translate expression

Get value out of DateTime column
if null to return String.Empty
else
DateTime.ToShortDateString
What am I doing wrong => query produced below:
var queryable = from p in Products
select new {
selldate = p.SellEndDate == null
? string.Empty
: p.SellEndDate.Value.ToShortDateString() };
Error: InvalidOperationException: Could not translate expression 'Table(Product).Select(p => new <>f__AnonymousType01(selldate = IIF((p.SellEndDate = null), Invoke(value(System.Func1[System.String])), p.SellEndDate.Value.ToShortDateString())))' into SQL and could not treat it as a local expression.
Basically what's happening here is that LINQ to SQL is taking your entire query and trying to convert it into something that SQL Server can understand. The problem, though, is that SQL Server has no concept of DateTime.ToShortDateString, so the conversion to SQL fails.
You'll have to change your query so that it just selects SellEndDate (which will get it as a Nullable<DateTime>) and then when you use the results of that query you can do the conversion to string. For example:
var list = (from p in Products
select p.SellEndDate).ToList();
// calling ToList() above means we have the entire resultset in memory and
// no longer have to pass the query back to SQL Server
var stuff = from p in list select new
{
selldate = p.SellEndDate == null ?
string.Empty :
p.SellEndDate.Value.ToShortDateString()
};
ToShortDateString doesn't seem to have equivalent SQL translation.
Use ToString instead.
If the date time field allows nulls:
from order in repository.Order
select order.OrdShipDate == null ? "" : order.OrdShipDate.GetValueOrDefault(DateTime.Now).Month.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Day.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Year.ToString();
If the date time field doesn't allow nulls:
from order in repository.Order
select order.OrdShipDate.Month.ToString() + "/" + order.OrdShipDate.Day.ToString() + "/" + order.OrdShipDate.Year.ToString();

Resources