I'm working right now to build LINQ up to the side where it needs to retrieve some information from the user about how many applications as you have got to be friends. - user exists in tablen.
venner Numberoffriends = db.venners.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0).Count();
if(Numberoffriends != null)
{
//true
}
it is such that it now makes mistakes here
db.venners.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0).Count();
ERROR ARE: The type 'int' can not be implicitly converted to 'LinqData.venner'
The error is because the return type of the Count() extension method is int, and you are trying to assign the return value to a LinqData.venner.
There is no implicit conversion from int to LinqData.venner, so that is the source of your error.
You can confirm this by refactoring your code into the following equivalent code:
int count = db.venners.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0).Count();
venner Numberoffriends = count;
if ( Numberoffriends != null )
{
//true
}
which will transfer your compilation error to the
venner Numberoffriends = count;
line.
It seems from your code that instead of having a count (using the Count() method), you want something like FirstOrDefault() or ToList(). This will enable you to do something with the venner object(s) you get.
I think the following would build, but I'm not sure about your intention and hence if it's what you want:
List<venner> venners = db.venners
.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0)
.ToList();
if ( venners.Count != 0 )
{
//true
// do something with venners here
}
What are you trying to do with your linq?
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 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.
I was originally using a foreach loop and then for each element in the loop, I perform a LINQ query like so:
foreach (MyObject identifier in identifiers.Where(i => i.IsMarkedForDeletion == false))
{
if (this.MyEntities.Identifiers.Where(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3).Any())
{
return false;
}
}
return true;
Then I modified it like so:
if (identifiers.Any(i => !i.IsMarkedForDeletion && this.MyEntities.Identifiers.Where(pi => i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3).Any()))
{
return false;
}
return true;
My question is this still the wrong way to use LINQ? Basically, I want to eliminate the need for the foreach loop (which seems like I should be able to get rid of it) and also make the DB query faster by not performing separate DB queries for each element of a list. Instead, I want to perform one query for all elements. Thanks!
You can change your code in this way, and it will be converted to SQL statement as expected.
To prevent runtime errors during transformation, it will be better to save DBSet to the IQueryable variable; identifiers should be IQueryable too, so you should change your code into something like this (to be honest, Resharper converted your foreach in this short labda):
IQueryable<MyObject2> identifiers = MyEntities.Identifiers.Where(i => i.IsMarkedForDeletion == false);
IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable();
return identifiers.All(identifier => !ids.Any(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3));
If identifiers is in memory collection you can change code in this way (hope that fields are string):
IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable();
string[] values = identifiers.Where(i => i.IsMarkedForDeletion == false).Select(i => String.Concat(i.Field1, i.Field2, i.Field3)).ToArray();
return !ids.Any(i => values.Contains(i.Field1 + i.Field2 + i.Field3));
Unfortunately your modified version will be executed exactly the same way (i.e. multiple database queries) as in the original foreach approach because EF does not support database query with joins to in memory collection (except for primitive and enumeration type collections), so if you try the most logical way
bool result = this.MyEntities.Identifiers.Any(pi => identifiers.Any(i =>
!i.IsMarkedForDeletion &&
i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3));
you'll get
NotSupportedException: Unable to create a constant value of type 'YourType'. Only primitive types or enumeration types are supported in this context.
The only way to let EF execute a single database query is to manually build a LINQ query with Concat per each item from in memory collection, like this
IQueryable<Identifier> query = null;
foreach (var item in identifiers.Where(i => !i.IsMarkedForDeletion))
{
var i = item;
var subquery = this.MyEntities.Identifiers.Where(pi =>
pi.Field1 == i.Field1 && pi.Field2 == i.Field2 && pi.Field3 == i.Field3);
query = query != null ? query.Concat(subquery) : subquery;
}
bool result = query != null && query.Any();
See Logging and Intercepting Database Operations of how to monitor the EF actions.
I would use it as follows:
if (identifiers.Where(i => !i.IsMarkedForDeletion &&
this.MyEntities.Identifiers.Field1 == i.Field1 &&
this.MyEntities.Identifiers.Field2 == i.Field2 &&
this.MyEntities.Identifiers.Field3 == i.Field3).Any()))
{
return false;
}
return true;
I hope this helps. Even though it is more to type out, it is more understandable and readable then using multiple 'where' statements.
I am using c#.net
I have two textboxes which if !empty need to be part of a WHERE clause within a LINQ query.
Here is my code
var result = from a in xxxx select a;
if(!string.IsNullOrEmpty(personName))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName)
}
else if(!string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.appStartDateTime >= dateFrom.Date)
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date);
}
I thought this would work but it doesn't like the .Where and I cant access the 'a' for example a.forename (The name 'a' does not exist in the current context)
What am I going wrong, or can this not actually be done?
Thanks in advance for any help.
Clare
Instead of this:
result.Where(a.forename.Contains(personName))
Try this:
result.Where(a => a.forename.Contains(personName))
You appear to be missing the Lambda operator (=>).
try this
var result = from a in xxxx select a
where (string.IsNullOrEmpty(personName) || a.forename.Contains(personName)
|| a.surname.Contains(personName))
&& (string.IsNullOrEmpty(dateFrom)
|| a.appStartDateTime >= DateTime.Parse(dateFrom).Date);
dateFrom appears to be a string so you have to parse it to get a date time.
This logic should work but I have not tested it. I could be wrong.
You seem to only care if the forename or surname contain personName if the personName is not null or empty. So you can rewrite it to return things if the personName is null or empty or the fore or sur name contains person name. Since the || operator is short circuiting it will not check Contains if the personName is null or empty.
You can also combine the predicates and make the logic shorter and easier to read:
var result = from a in xxxx select a;
if (!string.IsNullOrEmpty(personName))
result = result.Where(a => a.forename.Contains(personName) || a.surname.Contains(personName)
if (!string.IsNullOrEmpty(dateFrom))
result = result.Where(a >= a.appStartDateTime >= dateFrom.Date)
return result;
This method of combining predicates works well with 'AND' conditions. If you need to 'OR' conditions, it's a little bit more involved. Thankfully, Joe Albahari has created PredicateBuilder (as part of LINQKit) that helps with this greatly.
Hope this helps.
..or with just one exit point:
var result = from a in xxxx select a;
Func<string, bool> func = null;
if(!string.IsNullOrEmpty(personName))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName)};
}
else if(!string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.appStartDateTime >= dateFrom.Date};
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date;};
}
return result.Where(func);