I have a List property that I am setting like so:
testCard.LstSummaries =
db.Summaries.Where(
x =>
(x.AID == aId || x.AInformation.RegNumber == aRegNumber) && DbFunctions.TruncateTime(x.Day) == DateTime.Today.Date &&
x.deleted == false).ToList();
Then I have a conditional statement:
if (testCard.LstSummaries.Count > 0)
{
if (
testCard.LstSummaries.All(
x =>
(x.AID == aId || // ERROR HAPPENS ON THIS LINE
x.AInformation.RegNumber == aRegNumber) &&
DbFunctions.TruncateTime(x.Day) == DateTime.Today.Date && x.deleted == false))
{
// .... do something
}
I get an error:
This function can only be invoked from LINQ to Entities.
I want to avoid to make multiple calls to the database.. furthermore testCard.LstSummaries already has the values I am looking for.. but if I do this:
if (testCard.LstSummaries.Count > 0)
{
if (
db.Summaries.All(
x =>
(x.AID == aId || // NO ERROR
x.AInformation.RegNumber == aRegNumber) &&
DbFunctions.TruncateTime(x.Day) == DateTime.Today.Date && x.deleted == false))
{
// .... do something
}
I feel like making this call to the database is pointless because I would be retrieving the same results that are already stored in testCard.LstSummaries, but I can't invoke .All() because it's not LINQ to Entities.
Is there a workaround for this?
Problem is with DbFunctions.TruncateTime(x.Day), because it is converted to sql on runtime. Try to check without it.
Related
I have a complex where clause in my EF linq statement which repeats a subquery expression, on _db.OPESRRecoveryElements, but with different parameters, one of which is depending on records from the main entity, OPCases/OPCaseDto.
The query as it is works, but its hard for people to read. Ideally I'd like to be able to create an expression which could be re-used at the 3 necessary points and would still allow it to execute as a single, server-side SQL statement.
Is there a way to create an Expression / IQueryable definition which can be used for a subquery like this?
List<OPCaseDto> opCases = await _db.OPCases
.ProjectTo<OPCaseDto>(_autoMapperConfig, null, requestedExpands)
.Where(c =>
c.OPStatusId == OPStatusIds.AwaitingRecoveryElement
&& (
(c.OPCategoryLetter == "B"
// Only need a gross pensionable element if there is an outstanding gross pensionable figure
&& (c.GrossOverpaidPensionable - c.GrossRecoveredPensionable == 0
|| _db.OPESRRecoveryElements.Any(e => !e.NonPensionable && e.OPRecoveryMethod.OPTypeLetter == "G"
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery)
&& rp.AssignmentNo == c.RecoveryAssignmentNo)))
// Only need a gross non-pensionable element if there is an outstanding gross non-pensionable figure
&& (c.GrossOverpaidNonPensionable - c.GrossRecoveredNonPensionable == 0
|| _db.OPESRRecoveryElements.Any(e => e.NonPensionable && e.OPRecoveryMethod.OPTypeLetter == "G"
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery)
&& rp.AssignmentNo == c.RecoveryAssignmentNo))))
|| (c.OPCategoryLetter == "D"
// Don't need to check for an outstanding net figure - if the case is net and isn't complete, there will be one!
&& _db.OPESRRecoveryElements.Any(e => e.OPRecoveryMethod.OPTypeLetter == "N"
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery)
&& rp.AssignmentNo == c.RecoveryAssignmentNo)))
)
)
.AsNoTracking()
.ToListAsync();
If it wasn't for the c.RecoveryAssignmentNo part, I could easily create an expression like:
public Expression<Func<OPESRRecoveryElement, bool>> NoActiveRecoveryPlans(string opType, bool nonPen)
{
return e => e.OPRecoveryMethod.OPTypeLetter == opType
&& e.NonPensionable == nonPen
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery));
}
and use it like:
(c.OPCategoryLetter == "B"
// Only need a gross pensionable element if there is an outstanding gross pensionable figure
&& (c.GrossOverpaidPensionable - c.GrossRecoveredPensionable == 0
|| _db.OPESRRecoveryElements.Any(NoActiveRecoveryPlans("G", false)))
and it would get executed before the query to get the OPCases.
I could also fetch all the OPCaseDto records and OPESRRecoveryElements as separate queries and filter in memory, but I don't want to do that.
If I add a parameter to the function, string assignmentNo, I (unsurprisingly) get an error - "Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression3' to type 'System.Linq.Expressions.LambdaExpression'"
I have a .Any() Linq Method:
db.MyTable.Any(x => x.Year == MyObj.Year && x.Quarter == MyObj.Quarter && x.Week == MyObj.Week)
That is returning the error:
System.InvalidOperationException occurred
HResult=0x80131509
Message=Sequence contains no matching element
Source=EntityFramework
However the MSDN documentation states that the .Any method returns "true if the source sequence contains any elements; otherwise, false."
Why is this method throwing the exception instead of returning False?
With this little code it is fairly difficult to see what the cause is. It certainly is not in this part of the code.
Try to do some debugging, use the debugger to check all values, or write some Debugging statements before you perform your function:
// Check myObj
MyObjectType myObj = ... // instead of var, so you are certain you have the right type
Debug.Assert(myObj != null);
// only if Year, Quarter, Week are classes:
Debug.Assert(myObj.Year != null);
Debug.Assert(myObj.Quarter != null);
Debug.Assert(myObj.Week != null);
// check your db:
Debug.Assert(db != null);
Debug.Assert(db.MyTable != null);
int count1 = db.MyTable.Count();
int count2 = db.MyTable
.Where(x => x.Year == MyObj.Year
&& x.Quarter == MyObj.Quarter
&& x.Week == MyObj.Week)
.Count();
bool hasAny = db.MyTable
.Where(x => x.Year == MyObj.Year
&& x.Quarter == MyObj.Quarter
&& x.Week == MyObj.Week)
.Any();
// if all this works, your statement should also work
hasAny = db.MyTable
.Any(x => x.Year == MyObj.Year
&& x.Quarter == MyObj.Quarter
&& x.Week == MyObj.Week);
Is hard to see, with your example but probably you must check if you have Null values in "MyTable" and the datatypes of MyTable.Year,MyTable.Quarter and MyTable.Week match with the sames in MyObj...
I am trying to extend my linq query with additional search criteria to filter the data by sending also a List<Listitem> to the function for processing. The List can contain 1 or more items and the objective is to retreive all items which match any criteria.
Since i am sending several search criteria to the function the goal is to make a more accurate filter result the more information i am sending to the filter. If one or several criterias are empty then the filter will get less accurate results.
Exception is raised every time i execute following code, and I cant figure out how to solve the using statement to include the List<ListItem>. Appreciate all the help in advance!
Exception: Unable to create a constant value of type 'System.Web.UI.WebControls.ListItem'. Only primitive types or enumeration types are supported in this context.
using (var db = new DL.ENTS())
{
List<DL.PRODUCTS> products =
(from a in db.PRODUCTS
where (description == null || description == "" ||
a.DESCRIPTION.Contains(description)) &&
(active == null || active == "" || a.ACTIVE.Equals(active, StringComparison.CurrentCultureIgnoreCase)) &&
(mID == null || mID == "" || a.MEDIA_ID == mID) &&
(mID == null || objTypes.Any(s => s.Value == a.OBJECTS)) //Exception here!
select a).ToList<DL.PRODUCTS>();
return products;
}
Pass collection of primitive values to expression:
using (var db = new DL.ENTS())
{
var values = objTypes.Select(s => s.Value).ToArray();
List<DL.PRODUCTS> products =
(from a in db.PRODUCTS
where (description == null || description == "" || a.DESCRIPTION.Contains(description)) &&
(active == null || active == "" || a.ACTIVE.Equals(active, StringComparison.CurrentCultureIgnoreCase)) &&
(mID == null || mID == "" || a.MEDIA_ID == mID) &&
(mID == null || values.Contains(a.OBJECTS))
select a).ToList<DL.PRODUCTS>();
return products;
}
That will generate SQL IN clause.
Note - you can use lambda syntax to compose query by adding filters based on some conditions:
var products = db.PRODUCTS;
if (!String.IsNullOrEmpty(description))
products = products.Where(p => p.DESCRIPTION.Contains(description));
if (!String.IsNullOrEmpty(active))
products = products.Where(p => p.ACTIVE.Equals(active, StringComparison.CurrentCultureIgnoreCase)));
if (!String.IsNullOrEmpty(mID))
products = products.Where(p => p.MEDIA_ID == mID);
if (mID != null)
products = products.Where(p => values.Contains(p.OBJECTS));
return products.ToList();
Linq isn't able to convert the predicate on ListItem to something useful to Sql.
I would suggest that you pre-project the values of the ListItems into a simple List<string> before using this with Contains (which is converted to IN)
var listValues = objTypes.Select(_ => _.Value).ToList();
List<DL.PRODUCTS> products = ...
listValues.Contains(a.OBJECTS))
Let's say you have a List l_mur = new List();
And you populate the list.
Then based on conditions you want to REMOVE some values without requerying...
l_mur.RemoveAt(l_mur.FindIndex(f => (f.xid == tmur.xid && f.sid == tmur.sid && f.mid == tmur.mid && f.bid == tmur.bid)));
However, the code I used here, does not seem to work. It tells me index out of range, but how can it be out of range if I am just searching for something that truly does exist.
List<T>.FindIndex() returns -1 in case there is no match found - which is out of range for List<T>.RemoveAt().
Also note that FindIndex() only returns the index of the first occurrence based on your predicate - if there is more than one match you will only be able to delete the first one of them with your current approach.
A better approach to delete in place based on a predicate would be RemoveAll():
l_mur.RemoveAll(f => (f.xid == tmur.xid && f.sid == tmur.sid && f.mid == tmur.mid && f.bid == tmur.bid));
May be a good idea is to filter the list to a new instance of the list:
var l_mur = l_mur.Where(f => (f.xid != tmur.xid || f.sid != tmur.sid || f.mid != tmur.mid || f.bid != tmur.bid));
Use this code:
l_mur.Remove(l_mur.Find(f => (f.xid == tmur.xid && f.sid == tmur.sid && f.mid == tmur.mid && f.bid == tmur.bid)));
I have a linq statement that I want to add an additional where clause to if a drop down index is not 0.
people.Where(n.surname == "surname" || n.forename == "forename" && (dropdown.SelectedIndex > 0) ? n.id = dropdown.SelectedValue : n.id > 0).Select(n => n);
I am not even sure if what I am trying is possible??
I would like to do this rather than having to write two different statements.
Any ideas?
Thanks
Fortunately, this is easy because queries compose:
var query = people.Where(n.surname == "surname" || n.forename == "forename");
if (dropdown.SelectedIndex > 0)
{
query = query.Where(n => n.id.ToString() == dropdown.SelectedValue);
}