Linq dynamic query (Except - but SQL SERVER side) - c#

I want to return records where the date column is greater than DateTime.Now.AddDays(-3), but only if the type column is complete.
Meaning, I want to return everything except for records that are older than 3 days and the type is == complete (Enum 1).
It's almost as if I need Except, but for that, I'd need to enumerate the results, then I wouldn't have my page number of results (.Take(10)).
How would this be done in linq? I can't figure out how this query would look. I would like the query to be done SQL SERVER side as much as possible as to be efficient.
Just to be clear - I want all results, EXCEPT records that are completed and older than 3 days.
(completed meaning the type column value == 1 (Enum))
My project is .NetCore 3.1
Thanks

Could be this that you need ?
Var results = context.Entity.Where(x=> x.Type != enum.Completed ||( x.Type == enum.Completed && x.Date >=DateTime.Now.AddDays(-3)));

Maybe something like this?
Results = context.Entity.Where(x => x.Date > DateTime.Now.AddDays(-3) || x.Type != YourEnum.Complete);

I am not sure what you mean by "type" column, but I am guessing is the value of the object you are after.
Then you should probably do something like :
var dateRange = DateTime.Now.AddDays(-3);
var results = _repository.Entity.Where(w => w.Date >= date && w.Type != null);

Related

EF Core - Exclude from IQueryable if Datetime-Value is between 2 DateTimes from a list

I'm currently trying to achieve the following :
I have an IQueryable (UserDataDateRange) that has "from" and "to" DateTime-Values and another bigger IQueryable (UserData) with a DateTime-value and basically I want to exclude every data from userdata that Datetime-value is between the "from" and "to" comparing it to every UserDataDateRange-Entry.
Also every DateTime is nullable and I just want to ignore those.
Here is what I have tried :
private IQueryable<Userdata> ExcludeIfInDaterange(IQueryable<Userdata> query)
{
var dateRangeQuery = DBContext.UserDateDateRange.Where(x => x.From.HasValue && x.To.HasValue);
query = query.Where(l => !l.UserDate.HasValue);
foreach (var q in dateRangeQuery)
{
query = query.Where(l => l.UserDate.Value <= q.From.Value && l.UserDate.Value >= q.To.Value);
}
return query;
}
From my understanding this should work? Also I have tried avoid using something like "toArray" because from my understanding an IQueryable is basically the SQL that im manipulating and something toArray gives me the actual data.
However I really don't know what I'm doing wrong, theres no real exception, im just getting the following error :
Could not get function from a frame. The code is not available. The
error code is CORDBG_E_CODE_NOT_AVAILABLE, or0x80131309.
My function seems to break the query but i cant figure out why. I cant even use "Count()", it gives me the same error.
Anyone got an idea?
l.UserDate.Value <= q.From.Value && l.UserDate.Value >= q.To.Value
How can a date be both earlier than the From date and later than the To date at the same time? Unless you have some very odd data in your UserDateDateRange table, that filter will exclude all records.
You're also combining two mutually-exclusive filters with an AND operator, which is another way to exclude all records:
!l.UserDate.HasValue : UserDate is NULL
l.UserDate.Value <= ... : Can't possibly be satisfied, since UserDate is NULL
And there's no need to use .Value when comparing nullable properties with < / <= / > / >=.
Try something like:
private IQueryable<Userdata> ExcludeIfInDaterange(IQueryable<Userdata> query)
{
var dateRangeQuery = DBContext.UserDateDateRange.Where(x => x.From.HasValue && x.To.HasValue);
return query.Where(l => !l.UserDate.HasValue || !dateRangeQuery.Any(q => q.From <= l.UserDate && l.UserDate <= q.To));
}

LINQ: OrderByDescending by last value of multiple dates within the table

I am trying to do a OrderByDescending on my LINQ query. Currently I am doing OrderByDescending on ProcedureDate field.
Business Scenario:
A user can come in the application and add ChangeProcedureDates in application upto 3 times. So in database there are additional fields called ChangeProcedureDate1, ChangeProcedureDate2 and ChangeProcedureDate3.
What I am trying to accomplish:
I want to modify my LINQ query so it can check to see if in cases where ChangeProcedureDate1, ChangeProcedureDate2 and ChangeProcedureDate3 are NULL then OrderBy ProedureDate since there were no changes. If ProcedureDate, ChangeProcedureDate1 is filled and ChangeProcedureDate2 and ChangeProcedureDate3 are NULL then we need to OrderBy ChangeProcedureDate1. And same for ChangeProcedureDate2 and ChangeProcedureDate3. Basically I need to find the latest date entry among multiple DateTimeFields and do orderby on that.
Below is my LINQ query.
var unsortedItems = context.ReservationRequests
.Where(s => ((s.ReservationRequestStatusId.HasValue) &&
(s.ReservationRequestStatusId.Value == ReservationRequestStatusId)) &&
(Roles.Any(y => s.SubmitterGroupName == y) ||
s.CreatedBy == currentUserGuid))
.OrderByDescending(x => x.ProcedureDate ?? DateTime.MinValue);
I am having hard time trying to understand how can I do this in LINQ?
Try this:
.OrderByDescending(x => x.ChangeProcedureDate3 ?? x.ChangeProcedureDate2 ?? x.ChangeProcedureDate1 ?? x.ProcedureDate);
Or, change orders of properties as you need.

How to get data from linq query using select

I'm testing a simple LINQ select query and want to get two DateTime values from the table, but I'm doing something wrong here and need to know what I'm doing/thinking wrong?
My query:
var test = from x in db.Projects where x.ID == 1 select x;
Then I try to get on of the values like this:
DateTime Date = test. ?????
Here I thought I should get a suggestion from the Intellisense after the dot to pick the value from the column StartDate the table, but this isn't working.
If you need multiple matches...
Are you sure that you have multiple Project objects that have the same ID of 1 which your query currently suggests? If that is the case, then your query should return all of the records that meet that constraint via the Where() method :
// Get your Projects that meet your criteria
var test = db.Projects.Where(p => p.ID == 1);
If you need to access properties from these elements, you could either loop through them explicitly :
// Iterate through each match that was found
foreach(var t in test)
{
// Access your properties here
DateTime dt = t.YourDateProperty;
}
Or you could accomplish this using a Select() statement to only pull the properties that you need :
// This will return a collection of Dates mapped from each element in your collection
var testDates = db.Projects.Where(p => p.ID == 1)
.Select(x => x.YourDateProperty);
If you only need a single match...
If you only need to match a single element within your collection, you might consider using the First(), Single() or their equivalent FirstOrDefault() and SingleOrDefault() methods, which will return a single entity that you can use as expected :
// This will return the first Project with an ID of 1
var test = db.Project.FirstOrDefault(p => p.ID == 1);
// If it was successful
if(test != null)
{
// Then you can safely access it here
DateTime dt = test.YourDateProperty;
}
The only difference between the methods mentioned (normal vs OrDefault()) is that the OrDefault() methods will return null if no matching elements are found, so they generally require a null check as seen above.
test is going to be an enumeration (IEnumerable<>, IQueryable<>, etc... many are applicable) of your Project type. So if, for example, you want the first record, you might do this:
DateTime Date = test.First().SomeDateProperty;
All of the data returned from your query is in test. It could be zero records, one record, many records, etc.
In test you will have a collection which matches the condition x.ID == 1. You should iterate through that collection and take your needed properties.
Edit
I suggest you to use the syntax:
var result = db.Projects.FirstOrDefault(x => x.ID ==1);
this is such as:
var date = test.FirstOrDefault();
DateTime? Date = date != null ? date.StartDate : null;

Entity Framework 6 comparing two arrays or sequences

I have
//say "14,15"
string[] criterias = response.MultiChoiceId.Split(',');
In the database i have a column with "14,15" how can i use linq with entity framework to return those rows that match?
Also, if i have single value "1" how can i do similar query against above column/row so that it does not return "14,15" (because of the matching 1 in that string)
There are couple variations i.e. i would want "15,14" to match the row that has "14,15" as well right.
string testString = "14,15";
string[] match = testString.Split(',');
var searchRows = context.Table.ToList(); //Perhaps a where clause here so you don't need to return so many rows?
var result = searchRows.Where( r => match.All( s => r.ImportantField.ToString().Split(',').Contains(s));
This will ensure that all comma separated elements in the comparison string are in the interesting database column. If you want ONLY those elements, add an and condition with a check against the count:
var result = searchRows.Where( r => match.Length == r.ImportantField.ToString().Split(',').Length && match.All( s => r.ImportantField.ToString().Split(',').Contains(s));
As an aside to the performance characteristics of this, you are going to have to enumerate the whole collection no matter what, because you are using a where clause. The only downside to using "ToList" is that the whole thing could be kept in memory a little longer.
The less concise but more efficient query is to simply use an or in your Where statement much like you would if you were writing the query to get those rows in SSMS. If you want a more robust query you'll sacrifice some performance and because I am no LINQ to EF expert I will refrain from going into the details about how the code is handled by the LINQ provider/what sql is generated.
var results = MyContext.MyTable.Where(x => x.TheRow == "14,15" || x.TheRow == "15,14");
You can't use String.Split() in LINQ to Entities.
You could use the condition s == "14,15" || s == "15,14"
or if you want to support finding 14 and 15 in any list of values or could try:
s.StartsWith("14,") || s.Contains(",14,") || s.EndsWith(",14") && s.StartsWith("15,") || s.Contains(",15,") || s.EndsWith(",15").

How to use two conditions in Linq lambda which has different where clause

I want to query my item in table Items, where the last update of each item must be less than 91 days old (from last update till now) and the quantity > 0.
This is my code in the Model:
public IList<Item> GetAllProducts()
{
var ien_item = from i in this.DataContext.Items
orderby i.LastUpdated descending
select i;
return ien_item.ToList().Where(
s =>
HelperClasses.HelperClass.IsLastUpdate(s.LastUpdated.Value) == true
&&
(s => s.Quantity) > 0
)
.ToList();
}
Anyone can solve it? Thanks.
We don't really know what's not working here. EDIT: Merlyn spotted it; your lambda syntax is messed up. There's more to do here though.
However, I'd have thought you'd want this:
public IList<Item> GetAllProducts()
{
var lastUpdateLimit = DateTime.UtcNow.Date.AddDays(-91);
var query = from item in DataContext.Items
where item.Quantity > 0 && item.LastUpdated >= lastUpdateLimit
orderby item.LastUpdated descending
select item;
return query.ToList();
}
Note that this is able to do all the querying at the database side instead of fetching all the items and filtering at the client side. It does assume that HelperClasses.HelperClass.IsLastUpdate is simple though, and basically equivalent to the filter I've got above.
(One additional point to note is that by evaluating UtcNow.Date once, the result will be consistent for all items - whereas if your code evaluates "today" on every call to IsLastUpdate, some values in the query may end up being filtered against a different date to other values, due to time progressing while the query is evaluating.)
EDIT: If you really need to use HelperClasses.HelperClass.IsLastUpdate then I'd suggest:
public IList<Item> GetAllProducts()
{
var query = from item in DataContext.Items
where item.Quantity > 0
orderby item.LastUpdated descending
select item;
return query.AsEnumerable()
.Where(s => HelperClass.IsLastUpdate(s.LastUpdated.Value))
.ToList();
}
... then at least the quantity filter is performed at the database side, and you're not creating a complete buffered list before you need to (note the single call to ToList).
The problem is your lambda syntax. You're trying to define a second lambda while in the middle of defining a first lambda. While this is possible to do, and useful in some contexts, it is sort of an advanced scenario, and probably won't be useful to you until you know you need it.
Right now, you don't need it. Unless you know you need it, you don't need it :)
So -
Instead of what you've written:
.Where(
s =>
HelperClasses.HelperClass.IsLastUpdate(s.LastUpdated.Value) == true
&& (s => s.Quantity) > 0
)
Write this instead:
.Where(
s =>
HelperClasses.HelperClass.IsLastUpdate(s.LastUpdated.Value) == true
&& s.Quantity > 0 // Notice I got rid of the extra lambda here
)
If you're morbidly curious:
The compile error you got is because you didn't define your second lambda correctly. It redefined a variable you'd already used (s), and you were trying to check if a lambda was greater than zero. That makes no sense. You can only compare the result of a lambda to some value. It's like calling a function. You don't compare functions to numbers - you compare the result you get when calling a function to a number.
Easy ...
public IList<Item> GetAllProducts()
{
var ien_item =
from i in DataContext.Items
where
HelperClasses.HelperClass.IsLastUpdate(i.LastUpdated.Value)
&& s.Quantity > 0
orderby i.LastUpdated descending
select i;
return ien_item.ToList();
}
Linq to SQL: Methods are not allowed (linq is not magic and can not convert C# methods to TSQL)
http://msdn.microsoft.com/en-us/library/bb425822.aspx
Linq to Object: while looking the same, it is much more powerful than linq to SQL... but can not query SQL databases :)
http://msdn.microsoft.com/en-us/library/bb397919.aspx
Linq to XML: same as linq to Object, with xml object
http://msdn.microsoft.com/en-us/library/bb387098.aspx
Linq to Dataset: not the same as Linq to SQL !
http://msdn.microsoft.com/en-us/library/bb386977.aspx
Other linq providers:
http://en.wikipedia.org/wiki/Language_Integrated_Query

Categories

Resources