Linq statement fails with dynamic where clause - linq

I want to retrieve commissions with a certain order number.
This works:
var expression = from commission in db.Auftraege
where commission.Auftragsnummer == orderNbr
select new Commission() { EF_Commission = (commission as Auftrag) };
return expression.ToList();
However, if i transform this to use a dynamic where clause (because i want to apply some more filters), the where-clause does not seem to be applied. Instead, all commissions are returned instead of only those with a specific number:
//base query
var expression = from commission in db.Auftraege select new Commission() { EF_Commission = (commission as Auftrag) };
//now add a where clause if the input parameter was specified
if (orderNbr >= 0)
expression.Where(commission => commission.EF_Commission.Auftragsnummer == orderNbr);
return expression.ToList();
I have looked at a dozen examples but they all seem to do it this way. Does anybody have an idea why the second query ignores the where clause?

You need to assign the interim expression to something (perhaps to itself). expression.Where() does not alter the existing query - it returns a new one.
So:
expression = expression.Where(...);

Related

Dynamic Linq on DataTable error: no Field or Property in DataRow, c#

I have some errors using Linq on DataTable and I couldn't figure it out how to solve it. I have to admit that i am pretty new to Linq and I searched the forum and Internet and couldn't figure it out. hope you can help.
I have a DataTable called campaign with three columns: ID (int), Product (string), Channel (string). The DataTable is already filled with data. I am trying to select a subset of the campaign records which satisfied the conditions selected by the end user. For example, the user want to list only if the Product is either 'EWH' or 'HEC'. The selection criteria is dynaically determined by the end user.
I have the following C# code:
private void btnClick()
{
IEnumerable<DataRow> query =
from zz in campaign.AsEnumerable()
orderby zz.Field<string>("ID")
select zz;
string whereClause = "zz.Field<string>(\"Product\") in ('EWH','HEC')";
query = query.Where(whereClause);
DataTable sublist = query.CopyToDataTable<DataRow>();
}
But it gives me an error on line: query = query.Where(whereClause), saying
No property or field 'zz' exists in type 'DataRow'".
If I changed to:
string whereClause = "Product in ('EWH','HEC')"; it will say:
No property or field 'Product' exists in type 'DataRow'
Can anyone help me on how to solve this problem? I feel it could be a pretty simple syntax change, but I just don't know at this time.
First, this line has an error
orderby zz.Field<string>("ID")
because as you said, your ID column is of type int.
Second, you need to learn LINQ query syntax. Forget about strings, the same way you used from, orderby, select in the query, you can also use where and many other operators. Also you'll need to learn the equivalent LINQ constructs for SQL-ish things, like for instance IN (...) is mapped to Enumerable.Contains etc.
With all that being said, here is your query
var productFilter = new[] { "EWH", "HEC" };
var query =
from zz in campaign.AsEnumerable()
where productFilter.Contains(zz.Field<string>("Product"))
orderby zz.Field<int>("ID")
select zz;
Update As per your comment, if you want to make this dynamic, then you need to switch to lambda syntax. Multiple and criteria can be composed by chaining multiple Where clauses like this
List<string> productFilter = ...; // coming from outside
List<string> channelFilter = ...; // coming from outside
var query = campaign.AsEnumerable();
// Apply filters if needed
if (productFilter != null && productFilter.Count > 0)
query = query.Where(zz => productFilter.Contains(zz.Field<string>("Product")));
if (channelFilter != null && channelFilter.Count > 0)
query = query.Where(zz => channelFilter.Contains(zz.Field<string>("Channel")));
// Once finished with filtering, do the ordering
query = query.OrderBy(zz => zz.Field<int>("ID"));

LinqKit PredicateBuilder adding to linq query

I have a linq query which I want to add some additional, optional WHERE conditions to using LinqKit Predicate Builder. However, I an struggling to get the additional predicate to work
This is my initial query:
var query = (from OP in ctx.OrganisationProducts
where OP.OrganisationID == orgID
orderby OP.Product.Name, OP.Product.VersionName
select OP).Include("Product");
As you can see, there is JOIN in there.
I then wish to add additional predicates if required:
if(!includeDisabledP42Admin || !includeDisabledOrgAdmin)
{
var pred = PredicateBuilder.True<OrganisationProduct>();
if (!includeDisabledP42Admin)
pred.And(op => op.Enabled);
if (!includeDisabledOrgAdmin)
pred.And(op => op.AccessLevel == "NA" || op.AccessLevel == "NU");
query = query.Where(pred);
}
However, the generated SQL is unchanged and the query returns the same number of rows.
I thought I might have had to do the Expand conversion as so:
query = query.AsExpandable().Where(pred);
But this causes a runtime error.
I think the issue is the fact that I am adding the predicate to a query that is already no longer a pure OrganisationProduct object, however I would like advise on how I insert my predicate at the right place.
Thanks and all :-)
You have to assign the return value of And to the predicate:
pred = pred.And(op => op.Enabled);
Side note: you may like this predicate builder that works without Expand/AsExpandable, but has the same syntax.

How to add a second where clause to a linq expression

Trying to add a second where clause to a linq expression but it won't register.
var query = _dbSetBookedResource.AsQueryable<Resource>();
var resources = (from Resource in query where Resource.DateFrom == date select Resource)
if(true)
{
resources.Where(b => b.MemberId == currentUserId);
}
For some reason the second where clause won't register.
For some reason the second where clause won't register.
That's because you're not using the return value anywhere. That's just setting up a query, but then ignoring it. No LINQ methods change the value they're called on - instead they create a new query which has the appropriate filtering, projection etc.
You need:
resources = resources.Where(b => b.MemberId == currentUserId);
Also note that your initial query could be written more simply as:
var resources = query.Where(r => r.DateFrom == date);
Query expressions are overkill when all you want is a simple filter or projection.

Return Linq query results into List object

I am trying to return the results of a query into a List object, however the following code, as I normally use, does not work. Still relatively new to Linq, can someone explain the correct syntax/what's going on? This will work if I change the data type of productTraining to var...
List<AgentProductTraining> productTraining = new List<AgentProductTraining>();
productTraining = from records in db.CourseToProduct
where records.CourseCode == course.CourseCode
select records;
Select() and Where() will return IQueryable<T>, not List<T>. You've got to convert it to a List<T> - which actually executes the query (instead of just preparing it).
You just need to call ToList() at the end of the query. For example:
// There's no need to declare the variable separately...
List<AgentProductTraining> productTraining = (from records in db.CourseToProduct
where records.CourseCode == course.CourseCode
select records).ToList();
Personally I wouldn't use a query expression though, when all you're doing is a single Where clause:
// Changed to var just for convenience - the type is still List<AgentProductTraining>
var productTraining = db.CourseToProduct
.Where(records => records.CourseCode == course.CourseCode)
.ToList();

How to query and calculate dates in the where clause of a LINQ statement?

I am having trouble with the following piece of code. Before I paste it, Let me give a bit of history on what should happen.
I have a model containing 2 fields of interest at the moment, which is the name of the order the customer placed, and the date at which he/she placed it. A pre-calculated date will be used to query the dateplaced field (and should only query the dates , and not the time). The query counts the amount of duplicates that occur in the MondayOrder field, and groups them together. Now , when I exclude the where clause which should query the dates, the query runs great. However, The goal of this query is to count the amount of orders for the following week based on the date the order has been placed.
List<string> returnlist = new List<string>();
DateTime dt = getNextWeekMondaysDate().Date;
switch (day)
{
case DayOfWeek.Monday:
{
var CountOrders =
from x in Data.EntityDB.Orders
group x by x.MondayOrder into m
let count = m.Count()
select new
{
MondayOrderItem = m.Key, Amount = count
};
foreach (var item in CountOrders)
{
returnlist.Add(item.MondayOrderItem + " : " +
item.Amount);
}
}
break;
The getNextWeekMondaysDate() method has an overload which I can use, where if I supply it a date, it will get the following Monday's date from the parameter given. The problem is though, LINQ does not accept queries such as the following:
var CountOrders =
from x in Data.EntityDB.Orders
where getNextWeekMondaysDate(x.DatePlaced.Value).Date == dt
group x by x.MondayOrder into m
let count = m.Count()
select new { MondayOrderItem = m.Key, Amount = count };
This is exactly what I must achieve. Is there any workaround for this situation?
UPDATE
Here is the exception I get when I try the 2nd query.
LINQ to Entities does not recognize the method 'System.DateTime getNextWeekMondaysDate(System.DateTime)' method, and this method cannot be translated into a store expression.
You cannot do this directly, as user-defined method calls cannot be translated to SQL by the EF query provider. The provider recognizes a limited set of .NET methods that can be translated to SQL and also a number of canonical functions as well. Anything that cannot be expressed using these methods only is off-limits unless you write your own query provider (which is only theoretically an option).
As a practical workaround, you can calculate an appropriate range for x.DatePlaced.Value in code before the query and then use specific DateTime values on the where clause.
As an intellectual exercise, note that this method is recognized by the query provider and can be used as part of the expression. So this abomination should work too:
var CountOrders =
from x in Data.EntityDB.Orders
where EntityFunctions.AddDays(
x.DatePlaced.Date.Value,
(9 - DateAndTime.DatePart(DateInterval.WeekDay, x.DatePlaced.Value)) % 7)
.Date == dt
group x by x.MondayOrder into m
let count = m.Count()
select new { MondayOrderItem = m.Key, Amount = count };
Linq to Entities doesn't know how to convert arbitrary C# methods into SQL - it's not possible in general.
So, you have to work with the methods it does understand.
In this case, you could do something like this:
DateTime weekBegin = CalculateWeekBegin( dt );
DateTime weekEnd = CalculateWeekEnd( dt );
var CountOrders =
from x in Data.EntityDB.Orders
where x.DatePlaced.Value >= weekBegin && x.DatePlaced.Value < weekEnd
group x by x.MondayOrder into m
let count = m.Count()
select new { MondayOrderItem = m.Key, Amount = count });

Resources