Linq 2 Sql DateTime format to string yyyy-MM-dd - c#

Basically, i need the equivalent of T-SQL CONVERT(NVARCHAR(10), datevalue, 126)
I've tried:
from t in ctx.table
select t.Date.ToString("yyyy-MM-dd")
but it throws not supported exception
from t in ctx.table
select "" + t.Date.Year + "-" + t.Date.Month + "-" + t.Date.Day
but i don't think it's an usable solution, because i might need to be able to change the format.
The only option I see is to use Convert.ToString(t.Date, FormatProvider), but i need a format provider, and I'm not sure it works either
FormatProvider doesn't work, String.Format doesn't work (string.Format("{0:yyyy-MM-dd}", t.Date) throws not supported exception too).

In case someone else has this problem, I solved this problem by creating a seperate function to do the date formatting for me.
My Linq looks something like this:
from a in context.table
where ...
select new Class
{
DateString = GetFormattedString(a.DateTimeObject)
}
And GetFormattedString just returns DateTimeObject.ToString("yyyy-MM-dd");
This works for me!

Assuming that t.Date is nullable (DateTime?) this could be the problem, try using:
from t in ctx.table select (t.HasValue ? t.Date.Value.ToString("yyyy-MM-dd") : string.Empty );
Edit: Second try
The problem is the translation to SQL; it tries to translate the .ToString() to an SQL representation, and fails. So if you should do the following it should work:
(from t in ctx.table select t.Date).ToList().Select(d => d.ToString("yyyy-MM-dd"))
Or
(from t in ctx.table select t.Date).AsEnumerable().Select(d => d.ToString("yyyy-MM-dd"))
AsEnumerable() transforms the previously used IQueryable into an IEnumerable, thus stopping the generation of the SQL (in case of Linq to SQL) or any other transfromation by the provider implementing the specific IQueryable (e.g. Linq to SQL Provider).
Note, before calling AsEnumerable() you should have completed any actions that you want to be converted to SQL and executed on the database directly.

Is there a reason to perform the conversion on the database side? Whenever I run into this type of situation, I tend to just allow the database to give me the raw data and then do the massaging and manipulation within the application. Depending on the volume of requests to the database server and the size of the result set, I don't want to tie up processing and response time doing data conversions that can be handled by the client.

look no.3
var user = (from u in users
select new
{
name = u.name,
birthday = u.birthday.Value
})
.ToList()
.Select(x => new User()
{
name = x.name,
birthday = x.birthday.ToString("yyyyMMdd") // 0埋めされるよ
});

Try to create an object class that you can set the properties and let the properties be the value for your view.. Set the datetime data from LINQ as string and not datetime.. ex.
//Property class
[DataContract()]
public class Info
{
[DataMember(Name = "created_date")]
public string CreateDate;
}
//Controller
var date = from p in dbContext.Person select p;
CreateDate = Convert.ToDateTime(p.create_date).ToString("yyyy-MM-dd");
Hope you'll try this.. I have this on my past applications and this is what I did.

I had a global search function on a quoting website from which I wanted to be able to search on all details of a quote (quote references, vehicle details, customer details etc.) including the created dates using only the single input text value:
This means that I definitely don't want to enumerate the results before attempting to cast the date to the appropriate string format.
In an attempt to do this, I've come up with the following:
// this is obviously only a fragment of my actual query
_context.Quotes
.Where(q => string.Concat(
q.DateCreatedUtc.Day < 10 ? "0" : "",
q.DateCreatedUtc.Day,
"/",
q.DateCreatedUtc.Month < 10 ? "0" : "",
q.DateCreatedUtc.Month,
"/",
q.DateCreatedUtc.Year
)
.Contains(searchTerm));
I can confirm this translates to a database operation using EF Core V5 and Pomelo V5 with a MySql database.
The generated SQL looks something like this:
WHERE
LOCATE(#search_term, CONCAT(
CASE WHEN EXTRACT(DAY FROM `quote`.`date_created_utc`) < 10 THEN '0' ELSE '' END,
CAST(EXTRACT(DAY FROM `quote`.`date_created_utc`) AS CHAR),
'/',
CASE WHEN EXTRACT(MONTH FROM `quote`.`date_created_utc`) < 10 THEN '0' ELSE '' END,
CAST(EXTRACT(MONTH FROM `quote`.`date_created_utc`) AS CHAR),
'/',
CAST(EXTRACT(YEAR FROM `quote`.`date_created_utc`) AS CHAR)
)) > 0
This entire query has turned into a bit of a Frankenstein though and I am seriously questioning the value of allowing users to search on the dates.

try this
var select = from s in db.Table
where s.date == someDate
select new
{
date = DateTime.Parse(s.date.ToString()).ToString("yyyy-MM-dd"),
};

Related

LINQ to entites does not recognize Convert.ToDatetime method

EDIT All the other answers that link to a previous question's wont work for me because they are either using two tables or they know what startdate they are looking for.
I have the following LINQ query where i am trying to get the data from a table for the last 14 days. LINQ does not recognize Convert.ToDatetime method. The Query_Date column is a type string and i can't change it. How do i get the data i need?
var logs = (from bwl in db.UserActivities
where Convert.ToDateTime(bwl.Query_Date) >= DateTime.Now.AddDays(-14)
select new
{
Id = bwl.Id,
UserEmail = bwl.UserEmail
}).ToList();
So i couldn't get what i wanted because i would have to make too many changes so the workaround i am doing is: I am using this code
var logs = db.UserActivities.ToList().Take(100);
This will get me the last 100 entries. I will give options for more or less entries then they can filter it on date in the search bar on the datatable.
It's not great but time is against me and this will suffice.
Do you have data being entered at least every day? If so, what about something like this:
var oldestDate = DateTime.Now.AddDays(-14);
var dateString = oldestDate.ToString(*/ your date format */);
var oldestID = db.UserActivities.First(b => b.Query_Date == dateString).Id;
var logs = (from bwl in db.UserActivities
where bwl.Id > oldestID
select new {
Id = bwl.Id,
UserEmail = bwl.UserEmail
}).ToList();
Basically you find a record on that date and use its ID as a proxy for the date. This only works in certain circumstances (i.e. if the IDs are in date order).
This also isn't guaranteed to be the oldest entry on that date, but that could be achieved either by using ID order:
var oldestID = db.UserActivities.Where(b => b.Query_Date == dateString)
.OrderBy(b => b.Id)
.First().Id;
Or more lazily by using -15 instead of -14 when you add days, if you don't mind grabbing an unknown percentage of that 15th day.
Convert.ToDateTime works if your Query_Date has proper format. If not, check your string expression is convertable format.
I tested below code and it works fine when I assume Query_Date is a form of DateTime.toString().
var logs = (from bwl in db.UserActivities
where DateTime.Now - Convert.ToDateTime(bwl.Query_Date) <= TimeSpan.FromDays(14)
select new
{
Id = bwl.Id,
UserEmail = bwl.UserEmail
}).ToList();
I also tested with your where expression Convert.ToDateTime(bwl.Query_Date) >= DateTime.Now.AddDays(-14) and confirmed that it gives same result.

Linq query to db with dynamic where parameters

I have a table with data about cities:
In a search input field the user types an arbitrary number of chars then presses "search" button.
There is also another field where the user can enter the state via a dropdownlist.
Pressing the search button triggers an ajax call to a server controller which receives both the starting chars of the name and the two chars of the state.
The controller should return the list of cities with name starting with the supplied chars.
If the state is passed, the list should contain only those cities whose name starts with the supplied chars AND that are situated in the state.
If the state is not passed all matching cities are returned regardless of the state.
I cannot find a way to write a single Linq statement, because I do not know how to write the part of the query for the state:
Currently I do this:
public ActionResult selectCity(string searchString, string stateCode)
{
List<CityViewModel> mylist = new List<CityViewModel>();
IQueryable<City> mycities = null;
if (stateCode == "")
{
mylist = (from c in db.Cities
where c.name.StartsWith(searchString)
select c);
}
else
{
mylist = (from c in db.Cities
where ((c.name.StartsWith(searchString)) &&
(c.stateCode == stateCode))
select c);
}
<snip>
.....
.....
</snip>
return PartialView("_selComune",elenco);
}
I can't believe that there isn't a way to do this with a single query.
Yes, there is.
You want to say: if stateCode is empty or it matches City.stateCode then include it in result moreover City.Name must always begin with searchString. In code:
var mylist =
from c in db.Cities
where
c.name.StartsWith(searchString)
&& (stateCode == "" || c.stateCode == stateCode)
select c;
Roughly equivalent to this SQL (assuming parameters with proper syntax are provided):
SELECT * FROM Cities
WHERE name LIKE #searchString AND (#stateCode = '' OR stateCode = #stateCode)
SQL Server will optimize second comparison away if first condition is always satisfied (just check Query Execution Plan).
I think it's a typo writing code example but myList isn't List<City> unless you also add ToList() to your LINQ query.
Adriano Repetti's answer allows you to write a single query to handle "nullable" parameters but sometimes it has performance drawbacks, translated in SQL the kind of query may prevent indexes to work.
Please note that the following will also work and build the exact needed query on sql side, with only a bit of more code on LINQ side :
mylist = (from c in db.Cities
where c.name.StartsWith(searchString)
select c);
if (stateCode != "")
{
mylist = mylist.Where(c.stateCode == stateCode);
}
This kind of construct won't replace previous "where" content, it will add any new terms combining them with a "and" operator.
IQeryable is made to allow you to built it in many different lines, not a single one like you did in your code sample.

Date Difference Logic in LINQ

I'm attempting to access data from two different database tables and then join them together on two fields using LINQ in C#. I believe that I have a logically sound overall working approach. Part of the problem I'm running into is that I'm filtering the data from both tables prior to joining them, because the tables have far too much data and it would cause a crash.
The main problem is that for one of the tables I need to pull only data that has a timestamp (column) value of today. The timestamp value is of type System.DateTime?.
I've tried a few different ways:
DateTime? currentDate = System.DateTime.Now;
var second_data = (from b in this.database.table
where EntityFunctions.DiffDays(b.timeStamp.Value, currentDate) == 0
select b);
I'm under the impression this doesn't work because there's no function in the database to handle it. Inner Exception: '{"FUNCTION database.DiffDays does not exist"}'
var second_data = (from b in this.database.table
where b => b.timeStamp.Value.Date == DateTime.Now.Date
select b);
This doesn't work because 'The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
var second_data =
this.database.table.Where(sd => sd.timeStamp.Value.Date == DateTime.Now.Date);
But this again fails because of the use of .Date.
Unfortunately, because I don't have the memory to hold all that data, the possibility of of pulling all the data first and then running date logic on it is out of the question. If anyone could give any insight on how I might be able to solve this problem it would be greatly appreciated, thank you.
To get rows from the table that are only for today (or a specific date range), you could simply do this. The nice thing about this approach is that it works for both cases of a specific date or a date range.
// specify date range (without time)
DateTime currentDate = System.DateTime.Now.Date;
DateTime nextDate = currentDate.AddDays(1);
var second_data = from b in this.database.table
where b.timeStamp.Value >= currentDate
and b.timeStamp.Value < nextDate
select b;
query = query.Where(c=> DbFunctions.DiffDays(c.ToDate, DateTime.Now) < 30);
Its not working in my scenario
I'm using Sql Server and I get your first method to work if I remove the call to timestamp.Value. I don't think your version compiles because DiffDays takes nullable DateTimes for both parameters.
DateTime? currentDate = System.DateTime.Now;
var second_data = (from b in this.database.table
where EntityFunctions.DiffDays(b.timeStamp, currentDate) == 0
select b);
The other thing I note is that I get a warning:
'System.Data.Entity.Core.Objects.EntityFunctions' is obsolete: 'This
class has been replaced by System.Data.Entity.DbFunctions.'
So if it still doesn't work in MySql you could try DbFunctions instead
this answer helped me for the exact day number, not in-between difference:
Found it on forums.asp.net
Here's a sample showing one way to get all employees with a DOB between now and 14 days from now...
var employeesWithBirthday =
from emp in dc.Employees
let BirthdayDiff = (new DateTime(DateTime.Now.Year, emp.BirthDate.Month, emp.BirthDate.Day) - DateTime.Now).TotalDays
where BirthdayDiff >= 0 && BirthdayDiff <= 14
select emp
;
...although, be aware that a query like that will do a table scan (can't use any indexes)...

What is the way to join these two list?

I have a IList<User> that contains objects with a pair of value: Name and Surname.
On the database I have a table that contains rows with Name and Surname field. I want on codebehind to return the list of the rows that match my List, so let say have Name and Surname (respectively) equals.
My actual code is:
utenti = (from User utente in db.User.AsEnumerable()
join amico in amiciParsed
on new { utente.Nome, utente.Cognome } equals
new { Nome = amico.first_name, Cognome = amico.last_name }
select utente).OrderBy(p => p.Nome)
.OrderBy(p => p.Cognome)
.OrderBy(p => p.Nickname)
.ToList();
but this it is not good for two reasons:
It will download the whole records of the DB on the client;
I can't match Name and Surname as case sensitive (example Marco cordi != Marco Cordi); and on DB I have every kind of up/down chars.
As suggested on a previously question, seems that this answer can't help me, since I have to do a join (and also because the first problem it is not related).
What's the way to resolve this problem?
I don't know if this will work in your situation, but you might give it a try.
First, create a new list of strings:
List<string> amici = aimiciParsed.Select(x => x.first_name + "|" + x.last_name).ToList();
Then, select the users from DB, based on this list
var utenti = db.User.AsEnumerable().Where(utente =>
amici.Contains(utente.Nome + "|" + utente.Cognome)).ToList();
It sends the list of strings to the DB as a list of parameters and translates it into a query like
SELECT * FROM User WHERE User.Nome + "|" + User.Cognome IN (#p1, #p2, #p3 ...)
Unfortunately, there is no way to call Contains with something like StringComparison.OrdinalIgnoreCase, so you might have to change the collation of your columns.
This could be done with PredicateBuilder:
using LinqKit;
var predicate = PredicateBuilder.False<User>();
foreach(var amico in amiciParsed)
{
var a1 = amico; // Prevent modified closure (pre .Net 4.5)
predicate = predicate.Or(user => user.Nome == a1.first_name
&& user.Cognome == a1.last_name);
}
var query = db.User.Where(predicate.Expand())
.OrderBy(p => p.Nome)
...
The advantage is that indexes on Nome and Cognome can be used (which is impossible if you search on a concatenated value). On the other hand, the number of OR clauses can get very large, which may hit certain limits in SQL Server (https://stackoverflow.com/a/1869810/861716). You'll have to stress-test this (although the same goes for IN clauses).
When asking a question here on SO, you may want to translate it to English - don't expect people to know what "uente", "amico" or "Cognome" are.
One question: Why do you use ..in db.User.AsEnumerable() and not just ..in db.User?
Let everything in your query stay IQueryable (instead of IEnumerable). This lets Linq2Sql create SQLs that are as optimized as possible, instead of downloading all the records and joining the records client-side. This may also be the reason your search turns case-sensitive. Client-side in-memory string comparison will always be case-sensitive, while string comparison in SQL depends on the database's configuration.
Try ditching the .AsEnumerable() and see if you get better results:
utenti = (from User foo in db.User
join bar in amiciParsed
...

C# - Linq-To-SQL - Issue with queries

I am thoroughly frustrated right now. I am having an issue with LINQ-To-SQL. About 80% of the time, it works great and I love it. The other 20% of the time, the query that L2S creates returns the correct data, but when actually running it from code, it doesn't return anything. I am about to pull my hair out. I am hoping somebody can see a problem or has heard of this before. Google searching isn't returning much of anything.
Here is the linq query...
var query = from e in DataLayerGlobals.GetInstance().db.MILLERTIMECARDs
where e.deleted_by == -1
&& e.LNAME == lastName
&& e.FNAME == firstName
&& e.TIMECARDDATE == startDate.ToString("MM/dd/yyyy")
group e by e.LNAME into g
select new EmployeeHours
{
ContractHours = g.Sum(e => e.HRSCONTRACT),
MillerHours = g.Sum(e => e.HRSSHOWRAIN + e.HRSOTHER),
TravelHours = g.Sum(e => e.HRSTRAVEL)
};
This is the generated query....
SELECT SUM([t0].[HRSCONTRACT]) AS [ContractHours],
SUM([t0].[HRSSHOWRAIN] + [t0].[HRSOTHER]) AS [MillerHours],
SUM([t0].[HRSTRAVEL]) AS [TravelHours]
FROM [dbo].[MILLERTIMECARD] AS [t0]
WHERE ([t0].[deleted_by] = #p0)
AND ([t0].[LNAME] = #p1)
AND ([t0].[FNAME] = #p2)
AND ([t0].[TIMECARDDATE] = #p3)
GROUP BY [t0].[LNAME]
Now when I plug in the EXACT same values that the linq query is using into the generated query, I get the correct data. When I let the code run, I get nothing.
Any ideas?
What type is TIMECARDDATE? Date, datetime, datetime2, smalldatetime, datetimeoffset or character?
Any chance local date/time settings are messing up the date comparison of startDate.ToString(...)? Since you're sending #p3 as a string, 01/02/2009 may mean Feb 1st or January 2nd, depending on the date/time setting on the server.
My instinct is telling me that you need to be pulling out DataLayerGlobals.GetInstance().db.MILLERTIMECARDs into an IQueryable variable and executing your Linq query against that, although there really should be no difference at all (other than maybe better readability).
You can check the results of the IQueryable variable first, before running the Linq query against it.
To extend this concept a bit further, you can create a series of IQueryable variables that each store the results of a Linq query using each individual condition in the original query. In this way, you should be able to isolate the condition that is failing.
I'd also have a look at the LNAME & FNAME data types. If they're NCHAR/NVARCHAR you may need to Trim the records, e.g.
var query = from e in DataLayerGlobals.GetInstance().db.MILLERTIMECARDs
where e.deleted_by == -1
&& e.LNAME.Trim() == lastName
&& e.FNAME.Trim() == firstName
&& e.TIMECARDDATE == startDate.ToString("MM/dd/yyyy")
group e by e.LNAME into g
select new EmployeeHours
{
ContractHours = g.Sum(e => e.HRSCONTRACT),
MillerHours = g.Sum(e => e.HRSSHOWRAIN + e.HRSOTHER),
TravelHours = g.Sum(e => e.HRSTRAVEL)
};

Categories

Resources