Linq - between dates query - c#

I am trying to convert the following sql query as a linq query, however I keep experiencing an error -
Operator '<=' cannot be applied to operands of type 'string' and 'System.DateTime'.
SQL Query:
select top 3 Deal, [property], [event], [Date] from [dbo]. [Database_CRE_Events]
where (convert(datetime,[Date],103) between '01-May-2015' and '15-May-2015') and [property] is not NULL
order by convert(datetime,[Date],103) desc
I believe this is happening because c.Date is a string field from the entity database. I have tried converting the date values to string and datetime to get the following to work, but I keep getting an operand error.
LINQ Query:
DateTime dat = DateTime.Now.AddDays(-10);
string preWeek = dat.ToString("dd-MMM-yyyy");
DateTime dtt = DateTime.Now;
string today = dat.ToString("dd-MMM-yyyy");
var data = db.Database_CRE_Events.Where(c => c.Date <= Convert.ToDateTime(preWeek) && c.property != null)
.Select(x => new Loan() { Name = x.Deal, loan = x.property, evnt = x.Event })
.ToList().Take(3);
return data;
Is it possible to convert the original sql query to a linq query as c.Date being a string parameter?
Thank you for any further assistance.

The problem is that you're introducing strings into the mix for no reason at all. Unless you have to convert a DateTime to or from a string, don't do it.
Your query should be as simple as:
DateTime preWeek = DateTime.Today.AddDays(-10);
var data = db.Database_CRE_Events
.Where(c => c.Date <= preWeek && c.property != null)
.Select(x => new Loan() { Name = x.Deal, loan = x.property, evnt = x.Event })
.ToList()
.Take(3);
return data;
If c.Date is actually a string, you should fix your database so that it isn't a string. It's meant to be a date, so represent it as a date! If you absolutely have to keep it as a string, you should at least use a sortable format, such as yyyy-MM-dd. At that point you could use CompareTo - but it's horrible :(
If the format is dd-MMM-yyyy (as it sounds) you could try performing the parse in the LINQ query, still passing in a DateTime but parsing each value in the database:
.Where(c =>
DateTime.ParseExact(c.Date, "dd-MMM-yyyy", CultureInfo.InvariantCulture) <= preWeek
&& c.property != null)
... but I wouldn't be surprised if that fails. You may want to add a view in SQL which gives a more appropriate version of the data. Fundamentally, if you have to work with a broken schema (in this case using the wrong type and making a poor decision about how to format the data within that type) then you should expect pain. Pass this pain up to managers in order to prioritize changing the schema...
Notes:
You're fetching all the data, and then just taking the first three elements. That's a bad idea. Switch round the calls to ToList and Take after addressing the next bullet...
"First three elements" is only meaningful with ordering. Use OrderBy to specify an ordering
You're not currently using today, so I removed it
If you're only interested in a date, use DateTime.Today
You should carefully consider time zones, both in your database and in your calling code. This is currently using the system default time zone - is that what you want?

Related

Function date_trunc does not exist

I have a C# query using linq with two datetime fields Im trying to compare. The datetime field in the Postgres DB is stored as timestamp with time zone. However for some reason I am receiving "function date_trunc(unknown, timestamp with time zone, unknown) does not exist" error although in the debug view it shows I am passing the correct parameters. Please note: MyDate field is Nullable
C#
_context.MyDbObject.Where(a => DateTime.UtcNow.Date >= a.MyDate.Value.Date).AsQueryable();
Query shown in Debug View
(date_trunc('day', now(), 'UTC') >= date_trunc('day', a."MyDate", 'UTC')))
Error
MessageText: function date_trunc(unknown, timestamp with time zone, unknown) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
It is not direct answer to your question, but if you care about database indexes, do not use date truncation in queries. Your query can be rewritten.
var currentDate = DateTime.UtcNow.Date;
var endDate = currentDate.AddDays(1);
var query = _context.MyDbObject
.Where(a => a.MyDate.Value < endDate)
.AsQueryable();
// or records for current day
var query = _context.MyDbObject
.Where(a => a.MyDate.Value >= currentDate && a.MyDate.Value < endDate)
.AsQueryable();

Exception when comparing dates - Npgsql.PostgresException: '42883'

I'm trying to get records with a date that are the same or later than a given date. But this Exception keeps happening:
Npgsql.PostgresException: '42883: operator does not exist: character varying >= timestamp without time zone'
And here is my code:
var bairro = "test";
var dataBusca = DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd");
var buscaImovel = _context.ImovelModel
.Where(context => context.bairronome == bairro &&
context.datageracaoarq >= DateTime.Parse(dataBusca)).ToList(); //exception happens here
The dates in my database are using the "yyyy-MM-dd" timestamp. What could be the problem?
LINQ could not translate the expression if I casted the field to DateTime in it. The solution was changing the field type in the database to 'date'.

How to perform a date range filter on string with LINQ Query

I am trying to do a date range filter using the following linq:
IQueryable<Movies> movies= _context.Movies
.OrderByDescending(i => i.Id).Select(i => i);
DateTime startDate = Convert.ToDateTime(searchStartDate);
DateTime endDate = Convert.ToDateTime(searchEndDate);
movies = movies.Where(i => Convert.ToDateTime(i.TransDate) >= startDate &&
Convert.ToDateTime(i.TransDate) <= endDate)
.OrderByDescending(j => j.Id);
But it is't working and is giving me InvalidOperationException: The LINQ expression '...' could not be translated.
NOTE: The database I was given to work with has TransDate column in string format('YYYY-MM-DD'), hence this question.
While storing dates as string in database is not a good idea, at least the chosen format is orderable. And while EF Core does not provide translatable method for converting string to date, it allows you to have entity property of type DateTime (as it should have been), and map it to string column in database using value converter. Thus, you would write query against DateTime, and EF Core will convert the constant/parameter values to string and pass them to the SQL query.
Applying it to your case:
Model:
public class Movie
{
// other properties...
public DateTime TransDate { get; set; }
}
Configuration:
const string DateFormat = "yyyy-MM-dd";
modelBuilder.Entity<Movie>().Property(e => e.TransDate)
.HasConversion(
dateValue => dateValue.ToString(DateFormat, CultureInfo.InvariantCulture),
stringValue => DateTime.ParseExact(stringValue, DateFormat, CultureInfo.InvariantCulture)
)
.IsRequired()
.IsUnicode(false) // arbitrary
.HasMaxLength(10); // arbitrary
LINQ query usage:
IQueryable<Movie> movies = ...;
DateTime startDate = ...;
DateTime endDate = ...;
movies = movies
.Where(e => e.TransDate >= startDate && e.TransDate <= endDate);
Try removing one OrderByDescending you already have ordered it in the first statement. You are ordering the same query twice.
To understand the problem, you'll have to be aware of the differences between IEnumerable and IQueryable.
IEnumerable
An object of a class that implements IEnumerable<...> represents a sequence of similar objects. It holds everything to get the first element of the sequence, and as long as you've got an element, you can try to get the next one.
At its lowest level, this is done using GetEnumerator and repeatedly calling MoveNext() / Current, like this:
IEnumerable<Movie> movies = ...
IEnumerator<Movie> enumerator = movies.GetEnumerator();
// try to get the next element:
while (enumerator.MoveNext())
{
// There is a next element
Movie movie = enumerator.Current;
ProcessMovie(movie);
}
Deep inside foreach will do something like this. Every LINQ method that doesn't return IEnumerable<...> will also deep inside call GetEnumerator / MoveNext / Current.
An IEnumerable is meant to be executed by your process, hence the IEnumerable has access to all your procedures.
IQueryable
Although an IQueryable looks like an IEnumerable, it represent the potential to get an enumerable sequence.
For this, the IQueryable has an Expression and a Provider. The Expression holds what data must be fetched in some generic format; the Provider knows where he must fetch the data (Usually a database management system), and what language to use when fetching the data (usually SQL).
As long as you concatenate LINQ methods that return IQueryable<...> the Expression changes. The query is not executed, the database is not contacted. We say that the LINQ method uses deferred execution, or lazy execution. Concatenating such LINQ statements is not expensive.
The LINQ methods that don't return IQueryable<...>, like ToList(), ToDictionary(), FirstOrDefault(), Count(), Any(), are the expensive ones, just like foreach they will deep inside call GetEnumerator().
When you call GetEnumerator(), the Expression is sent to the Provider, who will translate the Expression into SQL and execute the Query. The fetched data is returned as an IEnumerator<...> of which you can call MoveNext() / Current.
Some Providers are smarter than others. For instance, some of them will not fetch the data when you get the Enumerator, but fetch it at the first MoveNext. Others won't fetch all requested data at once, but fetch the data "per page", so if you decide to enumerate only two or three items, then only the first page of Movies are fetched, not all 10000 of them.
But what has this to do with my question?
You use a method that your Provider doesn't know: Convert.ToDateTime. Hence it can't translate it into SQL. In fact, there are several LINQ methods that are not supported by entity framework. See Supported and Unsupported LINQ methods
Your compiler doesn't know how smart the Provider is, so your compiler can't complain. You'll get your error at runtime.
So you can't use Convert.ToDateTime, nor methods like Datetime.Parse.
What to do?
You wrote that the strings that you want to convert are in format 'YYYY-MM-DD'.
What you could do, is use string handling routines to separate them into "YYYY", "MM" and "DD". It depends on your provider whether you can use String.SubString.
Then use class DbFunctions to CreateDateTime
.Where(x => ... && DbFunctions.CreateDateTime(
x.TransDate.SubString(0,4), // YYYY
x.TransDate.SubString(5, 2), // MM
x.TransDate.SubString(8, 2), // DD
0, 0, 0) < endDate;
If your Provider also doesn't accept SubString, search for other methods to extract the YYYY, MM and DD. I know Sqlite has a Date method to convert strings to DateTime.
If you can't find any proper string manipulation routines, consider to convert endDate into format "YYYY-MM-DD" and return compare the TransDate string with the endDateSTring
string endDateString = String.Format("{0:D04}-{1:D02}-{2:D02}", endDate.Year, endDate.Month, ...)
Fiddle a bit with it, until you've got the correct format. Then use:
.Where(x => ... && x.TransDate < endDateString)
If that also does not work, create the SQL query yourself.

How to query datetime literals and null values in Linq predicate?

In my current query I'm having an error like this:
The datetime literal value '2012-05-24' is not valid.
For regular linq query it seems like this:
_listHistory = (from item in dbase.histories
where item.UserID == details.UserID && item.FriendID.HasValue == true && item.LogDate < today
select item).OrderByDescending(x => x.LogDate).Take(take).Skip(skip).ToList();
I will be dealing with number of table "Columns" so I have to use linq predicate:
string predicate = string.Format("it.UserID=={0} && CAST(it.{1} as Edm.Int64) != null && it.LogDate <= DATETIME'{2}'",
details.UserID, columnname, string.Format("{0:yyyy-MM-dd}", today));
_listHistory = dbase.histories.Where(predicate)
.OrderByDescending(x => x.LogDate).Take(take).Skip(skip).ToList();
But this query result the error above. Can anyone help me to construct my linq query?
It is my first time to deal with Linq predicates and literals.
You could likely use the dynamic LINQ support instead, but assuming this is Entity Query Language, it appears that you can construct the datetime with a call to CreateDateTime:
http://msdn.microsoft.com/en-us/library/bb738563.aspx
CreateDateTime( year, month, day, hour, minute, second)

Method using LINQ in my Repository that gets null errors

I have a Method in my LINQ
Basicly what my LINQ query does is that it retrieves data from my database.
A user can choose between two dates and retrieve the data between those two dates. But if a user choose on a date range which there is no data in I recieve this error:
The cast to value type 'Double' failed because the materialized value
is null. Either the result type's generic parameter or the query must
use a nullable type
It should be okey for the users to search between the two dates even if there is no data, it should give a error message that there is no data between those dates instead of this error I recieve in my Visual studio.
I am using MVC entity framework model first.
Here is the method:
public List<CoreValueAndAverageGrade> GetAverageGradeForAllCoreValues(
OfficeStatisticQueryViewModel model)
{
var StartDate = DateTime.Parse(model.StartDate);
var EndDate = DateTime.Parse(model.EndDate);
return db.CoreValue
.Where(v => v.CoreValueQuestion
.Any(q => !q.SubjectType.Ignored_Statistic))
.Select(coreValue => new CoreValueAndAverageGrade
{
CoreValue = coreValue,
AverageGrade = coreValue.CoreValueQuestion
.Where(q => !q.SubjectType.Ignored_Statistic)
.Average(q => q.SelectedQuestions
.Where(s =>
s.GoalCardQuestionAnswer != null
&& s.GoalCardQuestionAnswer.Grade.HasValue
&& s.GoalCard.Completed_Date >= StartDate
&& s.GoalCard.Completed_Date <= EndDate
)
.Average(s => s.GoalCardQuestionAnswer.Grade.Value))
})
.ToList();
}
Update: Grade is Double and Nullable
Any kind of help is very appreciated!
Thanks in advance!
Looking at the last part of the query - if there are no rows returned after filtering with the where clause, then s.GoalCardQuestionAnswer.Grade.Value will definitely throw because you're trying to access properties of an object that is null.
I suppose that if you change the code to this you would get 0 as the final value - then you'd need to check for this value explicitly in your code.
.Average(s => s != null ? s.GoalCardQuestionAnswer.Grade.Value : 0)
Otherwise, breaking the query down would be a good idea - it helps both debugging and readability of the code.
I would split up the queries and investigate further. My guess would be that there is some inconsistent data. Probably it happens somewhere where you compare a double with a double? and the double? value is null. Maybe check your records for nulls, which should actually be there.
That error usually comes up when you are trying to apply an aggregate operator to a result set with no records. Look at where you are using .Average and I am sure you have a query that returns no records right before.

Categories

Resources