I have a query that looks like this:
var TheQuery = (from....
where x.TheDate >= StartDate && x.TheDate <= EndDate
select new MyModel()
{
Total = (int?)x.Count() ?? 0,
....
}).Single();
Basically, I'm querying a number of records based between 2 dates. If for the date there are 0 values, it returns 0 as the Total. However, if there are no values at all, it returns null and crashes. I could add .SingleOrDefault() but it would return null instead of MyModel populated with a 0. The property Total is defined as an int.
How can I solve this?
Thanks
Count has an overload with a predicate, and returns 0 when no item matches the predicate
var result = new MyModel {
Total = <yourDataSource>
.Count(x.TheDate >= StartDate && x.TheDate <= EndDate)
};
if(TheQuery !=null || TheQuery .Count()>0){
//do something you wanna do
}
or
var v = TheQuery.ToList();
now check
if (v.Count > 0)
You should opt for:
int count = (from x in ...
where x.TheDate >= StartDate && x.TheDate <= EndDate
select c).Count();
That's what you want.
var TheQuery = (from....
where x.TheDate >= StartDate && x.TheDate <= EndDate
select new MyModel()
{
Total = (int?)x.Count() ?? 0,
....
}).DefaultIfEmpty(0).Single()'
Related
How to use Linq
A table
Field Birthday
Linq searches for recent birthdays (within 15 days)
from a in Employee where a.PositionStatus == true select new{ a.Name,a.Birthday}
Try below query
var fromdate = DateTime.Now;
var todate = DateTime.Now.AddDays(15);
var result =
(
from a in Employee
where a.PositionStuats==true && a.DateOfBirth.Value.Month >= fromdate.Month &&
a.DateOfBirth.Value.Month <= todate.Month &&
a.DateOfBirth.Value.Day >= fromdate.Day && a.DateOfBirth.Value.Day <= todate.Day
select new{ a.Name,a.Birthday}
).ToList();
Depending on your entity frame work version you can also replace a.DateOfBirth.Value.Month with a.DateOfBirth.Month.
I am building a WinForms application that searches for books in the Database.
I have four tables:
Author,Book,Country,Genre;
There is a field in "Book" table called "Year".
I added startDate and endDate fields, so it can search for specific book released between the given two years(startDate and endDate both are integers). That is where my troubles began.
This is how I parse inputs.
int startDateResult = 0;
int? startDate = null;
if (inputStartDate == string.Empty)
{
startDate = null;
}
else
{
if (Int32.TryParse(inputStartDate, out startDateResult))
{
startDate = startDateResult;
}
else throw new Exception("Please input correct year");
}
int endDateResult = 0;
int? endDate = null;
if (inputEndDate == string.Empty)
{
endDate = null;
}
else
{
if (Int32.TryParse(inputEndDate, out endDateResult))
{
endDate = endDateResult;
}
else throw new Exception("Please input correct year");
}
This is the LINQ query I am using for searching.
specificBookForAuthor = _context.Books.Where(c =>
(c.Author.Name.Contains(First) || c.Author.Surname.Contains(Last))
&& book==string.Empty?true: c.Name.Contains(book)
&& country == string.Empty ? true : c.Author.Country.Name.Contains(country)
&& genre == string.Empty ? true : c.Genre.Name.Contains(genre)
&& inputYear == string.Empty ? true : c.Year==year
&& inputStartDate == string.Empty ? true : c.Year >= startDate
&& inputEndDate == string.Empty ? true : c.Year <= endDate
).Select(b => b).ToList();
This query did not work. Then, I tried to comment all the lines except "inputEndDate", typed 0 in startDate and 5000 in endDate. After Debug, found out, that "specificBookAuthor"-s count was 1. Which was CORRECT.
Commented Code :
specificBookForAuthor = _context.Books.Where(c =>
(c.Author.Name.Contains(First) || c.Author.Surname.Contains(Last))
//&& book==string.Empty?true: c.Name.Contains(book)
//&& country == string.Empty ? true :
// c.Author.Country.Name.Contains(country)
// && genre == string.Empty ? true : c.Genre.Name.Contains(genre)
// && inputYear == string.Empty ? true : c.Year==year
// && inputStartDate == string.Empty ? true : c.Year >= startDate
inputEndDate == string.Empty ? true : c.Year <= endDate
).Select(b => b).ToList();
Did the same with inputStartDate(commented inputEndDate line and uncommented inputStartDate). Worked fine.
I get the problem when I leave both of the fields uncommented. Like this:
specificBookForAuthor = _context.Books.Where(c =>
(c.Author.Name.Contains(First) || c.Author.Surname.Contains(Last))
//&& book==string.Empty?true: c.Name.Contains(book)
//&& country == string.Empty ? true :c.Author.Country.Name.Contains(country)
// && genre == string.Empty ? true : c.Genre.Name.Contains(genre)
// && inputYear == string.Empty ? true : c.Year==year
&& inputStartDate == string.Empty ? true : c.Year >= startDate
&& inputEndDate == string.Empty ? true : c.Year <= endDate
).Select(b => b).ToList();
In that case, "specificBookAuthor"-s count is NULL instead of 1.
Could you test the following change which may not be directly related to the issue, but it can give you a more readable/debugable code?
Rewrite your code to this:
specificBookForAuthor = _context.Books.Where(c =>
(c.Author.Name.Contains(First) || c.Author.Surname.Contains(Last)));
if(!string.IsNullOrEmpty(book))
specificBookForAuthor = specificBookForAuthor.Where(c => c.Name.Contains(book));
if(!string.IsNullOrEmpty(country))
specificBookForAuthor = specificBookForAuthor.Where(c => c.Author.Country.Name.Contains(country));
//....and so on with your other conditions;
//....finally:
specificBookForAuthor = specificBookForAuthor.ToList();
As I said, this may not resolve your problem, but this way you can properly debug condition by condition and maybe then you can find the problem.
So every Book has a property Year, representing the year in which a book was published.
Furthermore you have two nullable integers startDate and endDate and you want to limit the selected books to books published between startDate and endDate, with special cases if one or both of these values are null.
You have an input IQueryable<Book> and as output you want the query that will only fetch the books between startDate and endDate, with special treatment if these values are null.
To keep it readable, testable and maintainable, I suggest an extension function. See Extension Methods Demystified
public static IQueryable<Book> WherePublishedBetween(this IQueryable<Book> books,
Year? start,
Year? end)
{
if (start.HasValue)
{
if (end.HasValue)
return books.Where(book => start.Value <= book.Year && book.Year <= end.Value);
else
return books.Where(book => start.Value <= book.Year);
}
else
{ // no start value
if (end.HasValue)
return books.Where(book => book.Year <= end.Value);
else
return books;
}
}
Usage:
int? startYear = ...
int? endYear = ...
var queryBooks = myDbContext.Books
.Where(book => book.Author.Name.Contains(First)
|| book.Author.Surname.Contains(Last)
&& ... // check other properties)
.WherePublishedBetween(startYear, endYear)
// continue with the query, with whatever LINQ you need
.Select(book => ...)
The LINQ expression builder will be smart enough to combine these two Where statements using Expression.AndAlso
x.CreateDate DateTime is stored in our database down to milliseconds. My dateTimePicker values startdate and enddate only allows for querying down to seconds.
How can change my query to ignore the milliseconds of x.CreateDate? I thought the code I wrote below would work but it is not.
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.AddMilliseconds(-x.CreateDate.Millisecond) == startdate)
.OrderByDescending(x => x.ID)
.Take(count);
var query = from l in _context.Logs
where l.ApplicationID == applicationId
&& SqlMethods.DateDiffSecond(l.CreateDate,startdate) == 0
orderby l.ID descending
select l).Take(count);
This avoids converting every date in you table into a string and the subsequent string comparison, by comparing the two dates as dates.
Getting CreateDate and startdate in the same format will help you compare apples to apples. This should accomplish that.
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.ToString(#"MM/DD/YYYY h:mm:ss") == startdate.ToString(#"MM/DD/YYYY h:mm:ss")
.OrderByDescending(x => x.ID)
.Take(count);
I have no idea why I could not get any results from the queries posted above as I tried several variations of their themes. However I did get it working correctly by adding milliseconds to the startdate and enddate variables and it s working.
if (stardDateIsValid && endDateIsValid)
startdate = startdate.AddMilliseconds(000);
enddate = enddate.AddMilliseconds(999);
query = _context.Logs.Where(x => x.ApplicationID == applicationId && x.CreateDate >= startdate && x.CreateDate <= enddate).OrderByDescending(x => x.ID).Take(count);
You can create extension method.
public const long TicksPerMillisecond = 10000;
public const long TicksPerSecond = TicksPerMillisecond * 1000;
public static bool IsEqualIgnoreMilliseconds(this DateTime date, DateTime compareDate)
{
long tickDiff = date.Ticks - compareDate.Ticks;
return tickDiff > 0 ? tickDiff < TicksPerSecond : tickDiff < -TicksPerSecond;
}
Then you can use this:
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.IsEqualIgnoreMilliseconds(startdate)
.OrderByDescending(x => x.ID)
.Take(count);
I want to write following query in Linq
INSERT INTO INOUTNEW (CODE,INDATE,TIME_DATE1,INOUTFLAG,TIME_FLD1,TIME_FLD2,TIME_FLD3)
SELECT CODE,MIN(INDATE),TIME_DATE1,'I',TIME_FLD1,TIME_FLD2,'31/05/2015' FROM INOUT
WHERE TIME_FLD1='T0003' AND INDATE >= '31/05/2015' AND INDATE <= '31/05/2015'
AND TIME_DATE1='31/05/2015'
GROUP BY CODE,TIME_DATE1,TIME_FLD1,TIME_FLD2
SO I am trying this :-
var data = ctx.tblInOut.Where(m => m.CompanyId == companyId && m.Time_Field1 == item.ShiftCode && m.InDate == StrInStart && m.InDate <= StrInEnd && m.Time_Date1 == InputDate).Select(m =>
new
{
EmployeeId = m.EmployeeId,
InDate = Min(m.InDate),
Time_Date1 = m.Time_Date1,
InOutFlag = m.InOutFlag
}).ToList();
I am stuck in Min Part. How to get Min in Select? And How to add multiple GroupBy in Linq?
Try something like this:
var data = ctx.tblInOut
.Where(m =>
m.CompanyId == companyId &&
m.Time_Field1 == item.ShiftCode &&
m.InDate == StrInStart &&
m.InDate <= StrInEnd &&
m.Time_Date1 == InputDate
)
.GroupBy(m =>
new {
m.Code,
m.Time_Date1,
m.Time_FLD1,
m.Time_FLD2
})
.Select(g =>
new
{
m.Key.Code,
InDate = m.Min(gg => gg.InDate),
m.Key.Time_Date1,
Something = "I",
m.Key.Time_FLD1,
m.Key.Time_FLD2,
SomeDate = "31/05/2015"
}).ToList();
To get Min, you must group first - otherwise it's trying to call Min on a single element.
.Key simply references the key of the group (in this case, a tuple of Code, Date1, Time_FLD1, Time_FLD2)
I'm trying to filter objects with SkipWhile but it does not evaluate multiple conditions.
Below is a sample code demonstrating the issue,
int[] numbers = { 1, 2, 3, 4, 5 };
var result = numbers.SkipWhile(n => n < 2 && n != 2).ToList();
This query selects 2,3,4,5, which omits the second condition (n != 2), when the first condition is true.
Is it possible to make the query evaluate both conditions?
Edit:
My actual condition is something like
... dateRanges
.OrderBy(d=>d.Sequence)
.SkipWhile(d => d.FromDate <= currentDate && d.ToDate >= currentDate)
.Skip(1).First();
which is operating on DateTime filed, to select next object in the list
Edit 2:
I have created a sample program, which is something similar to my actual code
Class to hold data,
public class DateRange
{
public int Sequence { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
}
Program
static void Main(string[] args)
{
var dateRanges = new List<DateRange>(){
new DateRange{Sequence = 1 , FromDate = new DateTime(2014,1,1), ToDate = new DateTime(2014,1,31)},
new DateRange{Sequence = 2 , FromDate = new DateTime(2014,2,1), ToDate = new DateTime(2014,2,28)},
new DateRange{Sequence = 3 , FromDate = new DateTime(2014,3,1), ToDate = new DateTime(2014,3,31)},
new DateRange{Sequence = 4 , FromDate = new DateTime(2014,4,1), ToDate = new DateTime(2014,4,30)},
new DateRange{Sequence = 5 , FromDate = new DateTime(2014,5,1), ToDate = new DateTime(2014,5,31)},
};
var myDate = new DateTime(2014, 2, 10); // A Date in Fabruary
//This query selects {2, 2014/2/1, 2014/2/28}
var selectedItem = dateRanges.OrderBy(d => d.Sequence)
.Where(d => d.FromDate <= myDate && d.ToDate >= myDate)
.First();
//What I actually need to select is {3, 2014/3/1, 2014/3/31}
//Which is next item of the list
//This is what i have tried
//But this query also selects {2, 2014/2/1, 2014/2/28}
var nextItem = dateRanges.OrderBy(d => d.Sequence)
.SkipWhile(d => d.FromDate <= myDate && d.ToDate >= myDate)
.Skip(1).First();
//Because, results of this part of query returns objects from {1, 2014/1/1, 2014/1/31} ...
var unexpectdItems = dateRanges.OrderBy(d => d.Sequence)
.SkipWhile(d => d.FromDate <= myDate && d.ToDate >= myDate);
}
It is evaluating both conditions - but as soon as the condition is false, the rest of the sequence is returned. As soon as n==2, n < 2 && n != 2 is false. In fact, your condition makes no sense anyway - if n is less than 2 it can't be equal to 2.
Basically it's not clear what you're trying to achieve, but the condition you're using isn't appropriate - and if you want to check your condition on every value rather than just "values until the condition isn't met" then you should use Where instead of SkipWhile.
EDIT: Now that you've posted a complete example, we can see what's wrong. Look at your condition:
SkipWhile(d => d.FromDate <= myDate && d.ToDate >= myDate)
Now look at the first item of your data:
new DateRange{Sequence = 1 , FromDate = new DateTime(2014,1,1),
ToDate = new DateTime(2014,1,31)},
And myDate:
var myDate = new DateTime(2014, 2, 10);
Is your condition satisfied by the first item of your data? No, because ToDate (January 31st) is not greater than or equal to myDate (February 10th). So no items are skipped by SkipWhile. Perhaps you wanted || instead of &&? (It's still not clear what this query is meant to achieve.)