Convert this code to LINQ? - c#

Sorry, Im just learning LINQ and am relatively new at it.
Is it possible to convert the following into LINQ?
foreach (DataRow gradeCount in GraceTable.Rows)
{
if (Convert.ToDecimal(obtMarksRow["Percentage"]) >=
(Convert.ToDecimal(gradeCount["EXG_MARKS_ABOVE"])) &&
(Convert.ToDecimal(obtMarksRow["Percentage"]) <=
Convert.ToDecimal(gradeCount["EXG_MARKS_BELOW"])))
{
string Grade = Convert.ToString(gradeCount["EXG_GRADE_NAME"]);
}
}
Edit : sorry i missed for each loop in ma query and obtMarksRow comes from one more loop which is outside this
I wrote the query like this
var gradeValue = from DataRow gradeRow in GraceTable.Rows
let marksAbove = gradeRow.Field<decimal>("EXG_MARKS_ABOVE")
let marksBelow = gradeRow.Field<decimal>("EXG_MARKS_BELOW")
where obtMarksRow.Field<decimal>("Percentage") >= marksAbove && obtMarksRow.Field<decimal>("Percentage") <= marksBelow
select gradeRow.Field<string>("EXG_GRADE_NAME");
but i am getting the value (gradeValue.ToString() ) as "System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Data.DataRow,System.String]"
Whats wrong ?

No, it isn't possible. As the commenters point out, LINQ is for querying collections of things. You don't appear to have a collection here: just an if statement and an assignment.
Furthermore, be careful about trying to convert things to LINQ unnecessarily. As you start to understand LINQ better, you'll find yourself naturally using it for a variety of purposes. But starting off with the assumption that code will be better with LINQ is probably a fallacy.
Edit
As mentioned earlier, LINQ is about querying a collection for a set of results. If you only want one result, you can use Single, First, SingleOrDefault, or FirstOrDefault to get it out of the resulting collection.
var gradeValues = from DataRow gradeRow in GraceTable.Rows
let marksAbove = gradeRow.Field<decimal>("EXG_MARKS_ABOVE")
let marksBelow = gradeRow.Field<decimal>("EXG_MARKS_BELOW")
where obtMarksRow.Field<decimal>("Percentage") >= marksAbove && obtMarksRow.Field<decimal>("Percentage") <= marksBelow
select gradeRow.Field<string>("EXG_GRADE_NAME");
var firstGradeValue = gradeValues.First(); // will throw exception if there were no matches.
Console.WriteLine(firstGradeValue);

Try the following:
var grades = from r in GraceTables.Rows
where obtMarksRow.Field<decimal>("Percentage") >=
r.Field<decimal>("EXG_MARKS_ABOVE") &&
obtMarksRow.Field<decimal>("Percentage") <=
r.Field<decimal>("EXG_MARKS_BELOW")
select r.Field<string>("EXG_GRADE_NAME");

You shouldn't use Linq per se but you should use the DatasetExtensions, brought in alongside linq, to get your columns from the DataRow in a type safe way without the need to convert them, i.e.
if (obtMarksRow.Field<decimal>("Percentage") >= (Convert.ToDecimal(gradeCount["EXG_MARKS_ABOVE"])) && etc...

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

What does this mean

in this piece of code what does C mean:
var Connection =
from C in Sessions
where (C.StartDate >= StartDate && C.StartDate < EndDate && C.User != null)
select new {C.User.UserName,
DG = C.Machine.DesktopGroup.Name};
https://blogsprod.s3.amazonaws.com/blogs/wp-content/uploads/2014/01/users-connected-to-Delivery-group-for-the-day.txt.gzip
Here, C represents each member of the Sessions collection. This is Linq syntax; see here for more.
If you tell us what type of class Sessions is we can help more.
It's roughly equal to this:
foreach (var c in sessions)
{
c.StartDate >= [....]
}
This is a LINQ expression. The "C" is a random letter or word you set at a beginning of the expression.
I think you might need to learn a bit more about LINQ expressions.

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

How can i use DateTime.AddXXXX functions in a Linq-to-Entities query?

I am trying to use AddMonths in a query
List<Entities.Subscriber> items = (from s in context.Subscribers
where s.Validated == false && s.ValidationEmailSent == true && s.SubscriptionDateTime < DateTime.Now.AddMonths(-1)
select s).ToList();
But I recieve an error :
LINQ to Entities does not recognize the method 'System.DateTime
AddMonths(Int32)' method, and this method cannot be translated into a
store expression.
Is there a way I can use this function inside my query?
The simplest fix to this is to work out the time limit once before using LINQ:
DateTime limit = DateTime.Now.AddMonths(-1);
List<Entities.Subscriber> items = (from s in context.Subscribers
where s.Validated == false && s.ValidationEmailSent == true &&
s.SubscriptionDateTime < limit)
select s).ToList();
Or more readably IMO:
var items = context.Subscribers
.Where(s => !s.Validated &&
s.ValidationEmailSent &&
s.SubscriptionDateTime < limit)
.ToList();
There's no benefit in using a query expression here, and explicit comparisons with true and false are ugly IMO (unless your properties are of type Nullable<bool> of course).
Jon Skeet has already provided a simple fix, but if you want the DateTime.Now.AddMonths bit to run on the database, try the EntityFunctions.AddMonths method.
This is a more general approach that is especially useful when you cannot replicate the expression cheaply or correctly on the client.
You can change your code to:
DateTime oneMonth = DateTime.Now.AddMonths(-1)
List<Entities.Subscriber> items = (from s in context.Subscribers
where s.Validated == false && s.ValidationEmailSent == true && s.SubscriptionDateTime < oneMonth
select s).ToList();
You have to do this because AddMonth is a .NET function that can't be translated into SQL by Linq to Entities. Perform the calculation in your code and then use the resulting datetime will work.

How do I convert this expression to LINQ?

Is it possible to convert this expression to LINQ?
TermsOfPayment termsOfPayment = null;
foreach (CustomerGroup group in _customer.CustomerGroups)
if (termsOfPayment == null) termsOfPayment = group.TermsOfPayment;
else if (group.TermsOfPayment != null)
if (group.TermsOfPayment.InvoiceDueDays < termsOfPayment.InvoiceDueDays)
termsOfPayment = group.TermsOfPayment;
It might seem like a stupid question since the expression above solves the question, but I use some LINQ expressions and am eager to lern more - hence the reason for this post.
Basically I just want to select the TermsOfPayment object with the minimum InvoiceDueDays (integer) value from the groups the customer is a part of.
termsOfPayment = (
from g in _customer.CustomerGroups
where g.TermsOfPayment != null
orderby g.TermsOfPayment.InvoiceDueDays
select g.TermsOfPayment
).FirstOrDefault();
var termsOfPayment =
_customer.CustomerGroups.OrderBy(cg=>cg.TermsOfPayment.InvoiceDueDays)
.First().Select(cg=>cg.TermsOfPayment);
Why not use aggregate, speed is better too:
var termsOfPayment =
_customer.CustomerGroups.Aggregate((a, n) => n.TermsOfPayment.InvoiceDueDays < a.TermsOfPayment.InvoiceDueDays ? n : a).TermsOfPayment;

Categories

Resources