Linq To Entities Internal Conversion - c#

I must to execute this expression:
var CAP = modelCap.AZCPC00F.Where(x => x.CPCVER == CPCVER && x.CPCNAR == CPCNAR && x.CPCCAP == CPCCAP)
.Where(x => XXXXKG == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLKG)) < XXXXKG)
.Where(x => XXXXMC == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLMC)) < XXXXMC)
.Where(x => XXXXFD == "N" ? true : x.CPCZFD == XXXXFD).FirstOrDefault();
When I try to execute this I have an exception on the internal conversion of x.CPCLKG. The exception is:
LINQ to Entities does not recognize the method 'int64' method, and this method cannot be translated into a store expression.
I know that the problem is in the conversion but how can i use this function?
An example of x.CPCLKG is 9.9999 and is an nChar character type.
Thanks to all

You need to have the logic that is not supported in Linq To Entities done in an in-memory query. We can do this by calling ToList() on the parts of the query that are supported. This will execute those parts, and return the results as a list. We can then execute whatever Linq supports on that list, in memory.
var CAP = modelCap.AZCPC00F.Where(x => x.CPCVER == CPCVER && x.CPCNAR == CPCNAR && x.CPCCAP == CPCCAP)
.Where(x => XXXXFD == "N" ? true : x.CPCZFD == XXXXFD)
.ToList();
var CAP2 = CAP.Where(x => XXXXKG == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLKG)) < XXXXKG)
.Where(x => XXXXMC == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLMC)) < XXXXMC).FirstOrDefault()

Related

how to conditionally add where condition in linq [duplicate]

This question already has answers here:
Linq dynamically adding where conditions
(3 answers)
Closed 2 years ago.
I am using entity framework and i want to add a where condition only if the condition is met else i dont want that where to be executed which i want to do it in 1 query
Context.Test
.Where(v => v.Valuation.ValuationPeriod.PeriodName == periodName && parameters.SelectedTemplateIds.Contains(v.TemplateTypeId) && fundBusinessIds.Contains(v.Fund.BusinessId))
.Where(v => parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode))
.Where(v => wpGroup == null || v.Valuation.PortfolioCompany.WPGroup == wpGroup)
.Where(v => !myDeals || v.Valuation.PortfolioCompany.UserPermission.Any(up => up.UserId == userId))
so in the above query
Where(v => parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode))
I want to execute only if
parameters.sortedList.Count() > 0
else i dont want to include that where condition in the above query. So basically here i know we can do by seperating the query that is first get a list of values and then check this condition and if it matches then add a where on that list. But is it possible without doing that?
The filter could be added contitionally as follows by using an intermediate assignment.
var result = Context.Test.Where(v => v.Valuation.ValuationPeriod.PeriodName == periodName && parameters.SelectedTemplateIds.Contains(v.TemplateTypeId) && fundBusinessIds.Contains(v.Fund.BusinessId));
if (parameters.sortedList.Count() > 0)
{
result = result.Where(v => parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode));
}
result = result.Where(v => wpGroup == null || v.Valuation.PortfolioCompany.WPGroup == wpGroup)
.Where(v => !myDeals || v.Valuation.PortfolioCompany.UserPermission.Any(up => up.UserId == userId));
You can use this
.Where(v => parameters.sortedList.Count() > 0 ?
parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode) : true)
LINQ queries don't execute unless you do something that causes enumeration of the resulting IEnumerable, which means you can have a pattern of:
var x = collection.Where(...);
if(sometest)
x = x.Where(..);
var result = x.ToList(); //enumerate, causing evaluation
If sometest is true then both the Where will apply (AND style; both where predicates would have to result in a true for a collection entity to appear in the result), if sometest is false, then only the first Where applies
You can add an extension
public static class WhereIfExtension
{
public static IEnumerable<TCollection> WhereIf<TCollection>(this IEnumerable<TCollection> source,
bool condition, Func<TCollection, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
public static IQueryable<TCollection> WhereIf<TCollection>(this IQueryable<TCollection> source, bool condition,
Expression<Func<TCollection, bool>> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
then
Context.Test
.Where(v => v.Valuation.ValuationPeriod.PeriodName == periodName && parameters.SelectedTemplateIds.Contains(v.TemplateTypeId) && fundBusinessIds.Contains(v.Fund.BusinessId))
.WhereIf(parameters => parameters.sortedList.Count() > 0, parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode))
.Where(v => wpGroup == null || v.Valuation.PortfolioCompany.WPGroup == wpGroup)
.Where(v => !myDeals || v.Valuation.PortfolioCompany.UserPermission.Any(up => up.UserId == userId))

Conditioning LINQ Query

Currently I have a LINQ query like this:
var policy = snapshotDate == null
? await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync()
: await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber)
&& (p.PolicyEffectiveDate <= snapshotDate && p.PolicyExpirationDate > snapshotDate))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync();
I would like to know if I can shorten it like this:
var policy = await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber))
.Where(p => snapshotDate != null && (p.PolicyEffectiveDate <= snapshotDate && p.PolicyExpirationDate > snapshotDate))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync();
But this doesn't work and I would like to know how I can condition this LINQ query in a proper way and shorten them without using the terenary operator.
Thanks in advance.
Try this:
var policy = await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber)
&& (snapshotDate == null ||
(p.PolicyEffectiveDate <= snapshotDate &&
p.PolicyExpirationDate > snapshotDate)))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync();
This query will apply conditions of PolicyEffectiveDate and PolicyExpirationDate if snapshotDate is not null otherwise only PolicyNumber condition will apply.
Seems like you want your condition to evaluate
snapshotDate == null || (p.PolicyEffectiveDate <= snapshotDate && p.PolicyExpirationDate > snapshotDate)
Which says, give all dates when snapshotDate is null, or within when it's not.
Note : about multiple where clauses. In a memory based collection you are will get a performance increase combining them with an && (due to the extra delegate invocation, and potential multiple enumerations), however with most LINQ to Query providers it will evaluate in the exact same SQL. Though in general it's best combining these if you can.

LINQ to Entities does not recognize the method - subquery count

Hi this question has been asked before but I'm struggling to find an answer that matches my question (the error comes up a lot).
This looks to be similar but with 4 years ago has there been any progress.
LINQ to Entities does not recognize the method 'Int32
Basically I want to move a subquery that returns a count into a function anyway here's the code.
vm.SicknessEpisodes = _db.SicknessEpisodes.Where(x => x.Status == true && x.LastDay == null).OrderByDescending(x => x.FirstDay).
Select(x => new HomeSicknessEpisode
{
SicknessEpisode = x,
EpisodesIn12Months = _db.SicknessEpisodes.Where(y => y.StaffID == x.StaffID && y.Status == true &&
(y.LastDay == null || (y.LastDay.HasValue && y.LastDay >= prevDate))).Count()
}).
ToPagedList(p, 100);
This works
This
vm.SicknessEpisodes = _db.SicknessEpisodes.Where(x => x.Status == true && x.LastDay == null).OrderByDescending(x => x.FirstDay).
Select(x => new HomeSicknessEpisode
{
SicknessEpisode = x,
EpisodesIn12Months = _db.episodes12Months(x,prevDate).Count()
}).
ToPagedList(p, 100);
public IQueryable<SicknessEpisode> episodes12Months(SicknessEpisode x, DateTime prevDate)
{
return this.SicknessEpisodes.Where(y => y.StaffID == x.StaffID && y.Status == true &&
(y.LastDay == null || (y.LastDay.HasValue && y.LastDay >= prevDate)));
}
Does not.
Can anyone tell me how I can make it work - ideally without going into a List and then foreach each episode to run the count.
I'm just trying to make it as efficient as possible for Entity Framework.
If it's not possible - then that's fine - one of those things.

Rewrite sql query to LINQ. Can't find an error

This is an SQL query:
SELECT Website,VendorID,Name,LinkProduct,
Link,Logo,Image,NameExtra as Industry,
(SELECT [Percent] FROM Web_Promotion
WHERE Web_Promotion.VendorID=Web_Vendor.VendorID)
AS PercentOff
FROM Web_Vendor WHERE Active='1' AND
(VendorID IN (Select VendorID FROM Web_Promotion
WHERE VendorID<>'' AND Static='True' AND [Percent] <> '0' AND
((Expires>=GETDATE()) OR (Expires IS NULL))) OR
VendorID IN (SELECT TOP 1 SC1 FROM NavItems
WHERE SC1=Web_Vendor.VendorID AND Promotion<>''
AND ((PromotionStart<=GETDATE() AND PromotionEnd>=GETDATE())
OR (PromotionStart<=GETDATE() AND PromotionEnd IS NULL))))
ORDER BY NameExtra,Sequence
I need to rewrite it to LINQ. So this is my LINQ:
return await _db.Web_Vendor.
Where(x => !(x.WebPromotion.VendorID == string.Empty || x.WebPromotion.VendorID == null)
&& x.WebPromotion.Static == true && x.WebPromotion.Percent != 0 &&
(x.WebPromotion.Expires >= DateTime.Now || x.WebPromotion.Expires == null)
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Take(1).Contains(x.WebPromotion.VendorID)))
.Include(x => x.WebPromotion).Where(x => x.Active == true).OrderBy(x => x.NameExtra)
.ThenBy(x => x.Sequence).ToListAsync();
I spent about three ours, but can't find an error. Original SQL query returns 16 rows, but my LINQ code returns only 13 of the. Unfortunately I have only one navigation property (Web_Vendor <-> Web_Promotion). I think that an error in the second part of my query:
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Take(1).Contains(x.WebPromotion.VendorID)))
Can any expert check my code and help me?
Correct data:
http://prntscr.com/9a5xwu
Linq data (not correct) contains the same data as correct instead of values where PercentOff is null.
The main problem is that LINQ generate inner join instead of left join in this place: http://prntscr.com/9a6stb
since you say that your linq data miss the case when PercentOff = null, i'd focus on that
I guess your "PercentOff" in Linq is Percent property, and i see that you have in your where: "x.WebPromotion.Percent != 0"
Is that a nullable value or you convert the null to the default property type, that is 0?
couldn't be that null is converted to 0 and then the query skip it?

Entity Framework: Unable to create a constant value of type 'System.Collections.Generic.IList`1'

This has caused me no end of problems today. I have this simple query
var result =
DataContext.Accommodations.Where(a =>
(criteria.MinPrice == null || a.AccommodationRates.Any(r => r.From >= criteria.MinPrice)) &&
(criteria.MaxPrice == null || a.AccommodationRates.Any(r => r.To <= criteria.MaxPrice)) &&
(criteria.Locations == null || criteria.Locations.Count == 0 || a.AccommodationPlaceJoins.Any(j => criteria.Locations.Contains(j.Place.PlaceName)))
);
The last line of this query is causing me problems
(criteria.Locations == null ||
criteria.Locations.Count == 0 ||
a.AccommodationPlaceJoins.Any(j => criteria.Locations.Contains(j.Place.PlaceName)))
The error it gives is
Unable to create a constant value of type
'System.Collections.Generic.IList`1'. Only primitive types ('such as
Int32, String, and Guid') are supported in this context.
I'm not even trying to create a list. All I'm trying to do here is bring back accommodations which are associated to a place (where the place name in the Place table which is linked to the Accommodation table via the AccommodationPlaceJoin table) is equal to any one of the place names in criteria.Locations (which is of type IList).
I've tried changing this line to this, but it didn't work.
(criteria.Locations == null ||
criteria.Locations.Count == 0 ||
a.AccommodationPlaceJoins.Any(j => criteria.Locations.Any(l => l == j.Place.PlaceName)))
The constant value EF can't create is null for the comparison criteria.Locations == null. You need to split the query into two cases and do the check for the empty list outside of the query, for example like so:
var result = DataContext.Accommodations.Where(a =>
(criteria.MinPrice == null ||
a.AccommodationRates.Any(r => r.From >= criteria.MinPrice)) &&
(criteria.MaxPrice == null ||
a.AccommodationRates.Any(r => r.To <= criteria.MaxPrice)));
if (criteria.Locations != null && criteria.Locations.Count > 0)
{
result = result.Where(a => a.AccommodationPlaceJoins
.Any(j => criteria.Locations.Contains(j.Place.PlaceName)));
}
Edit
BTW: Composing the whole query would make it better readable in my opinion and will simplify the SQL that has to be sent to the database:
IQueryable<Accommodation> result = DataContext.Accommodations;
if (criteria.MinPrice != null)
result = result.Where(a => a.AccommodationRates
.Any(r => r.From >= criteria.MinPrice));
if (criteria.MaxPrice != null)
result = result.Where(a => a.AccommodationRates
.Any(r => r.To <= criteria.MaxPrice));
if (criteria.Locations != null && criteria.Locations.Count > 0)
result = result.Where(a => a.AccommodationPlaceJoins
.Any(j => criteria.Locations.Contains(j.Place.PlaceName)));
I had this same issue when trying to pass a list of strings for comparison into an EF core query recently and found that it resolves the issue to assign an empty array or empty list to the value in case it is null rather than doing the null check right in the EF Core query.
This allows you to query the EF Core database without the null error and you don't need a bunch of conditional branching for separate queries - just one query with a bit of null coalescence.
So your code fixed this way would look like this:
var locations = criteria.Locations == null ? new List<Location>() : criteria.Locations;
var result =
DataContext.Accommodations.Where(a =>
(criteria.MinPrice == null || a.AccommodationRates.Any(r => r.From >= criteria.MinPrice)) &&
(criteria.MaxPrice == null || a.AccommodationRates.Any(r => r.To <= criteria.MaxPrice)) &&
(locations.Any() ? a.AccommodationPlaceJoins.Any(j => criteria.Locations.Contains(j.Place.PlaceName))) : true);
Keep in mind I am not sure of the type for locations or accomodationplacejoins so I cannot be sure the above code will exactly work but the general idea is we are doing a .Any() instead of checking for null. This way we can keep our query a little simpler. We are then using a ternary expression within the last block of our where clause so if the locations list turns out to be empty, we skip the condition entirely and just return true which coalesces the empty list if it is empty.

Categories

Resources