Use ?: Operand on entity framework - c#

I want to use it in this way
Context.Moves.OfType<Cuotes>().Where(p =>
p.Final == true
&& dtFrom.DateTime != null ? p.DeliverDate >= dtFrom.DateTime : true
&& dtTo.DateTime != null ? p.DeliverDate <= dtToHasta.DateTime : true
&& !ckShowDelivered.Checked ? p.Delivered == false : true
&& !ckShowDelivered.Checked ? p.Canceled == false : true
);
Due I have muliple filters I tried to manipulate in this way, actually I have another checkbox to condition if old Cuotes versions are showed with Final == false statement, but this operand inside the Where clause seems to be not working.
It is a way to apply this operand or I must to hard code the if conditions for each combination of options posible?

You could just chain those in separate Where calls:
var result = Context.Moves.OfType<Cuotes>().Where(p => p.Final == true);
if (dtFrom.DateTime != null)
result = result.Where(p => p.DeliverDate >= dtFrom.DateTime);
if (dtTo.DateTime != null)
result = result.Where(p => p.DeliverDate <= dtToHasta.DateTime);
if (!ckShowDelivered.Checked)
result = result.Where(p => !p.Delivered);
if (!ckShowDelivered.Checked)
result = result.Where(p => !p.Canceled);
That way, you can use any kind of external conditions to affect your query without having to use those external conditions within the query itself.

Related

Null value is still evaluated after testing it with condition

I have the following LINQ query:
houses.Where(x =>
x.A && user.B
||
(x.Address == null || user.Address == null ? false : x.Address.CountryCode == user.Address.CountryCode
))
I get an error when user.Address is null in:
user.Address.CountryCode
To avoid that I tried to use:
x.Address == null || user.Address == null
But it seems user.Address.CountryCode is still evaluated.
How can I avoid that?
It's definitely EF Core 3.0 query translator defect.
For now (and in general) you can avoid it by not using conditional operator ?: for criteria expressions which need to be converted to SQL query parameters (like your user.Address.CountryCode), but the equivalent logical binary expressions.
e.g. instead of
(x.Address == null || user.Address == null ? false : x.Address.CountryCode == user.Address.CountryCode)
use the equivalent
(x.Address != null && user.Address != null && x.Address.CountryCode == user.Address.CountryCode)
Personally I find this logic confusing. You might try:
( (x?.Address?.CountryCode != null && user?.Address?.CountryCode != null) ? x.Address.CountryCode == user.Address.CountryCode : false
If that doesn't work then there is something very odd going on with the Address or CountryCode properties! Probably and entity framework issue.

LINQ query needed using a conditional operand in the WHERE clause

I am having trouble composing a LINQ statement that uses a conditional operand within the where clause and have looked through this site for a similar problem without any luck. I'm sure there is a better way to express this query. The value XXX within the is the result of determining which field in the object to use for comparison and is explained below.
Below is what I am trying to accomplish:
var paymentReceivedAmt = (from registerEntry in RegisterEntries
where registerEntry.TransactionType != null
&& registerEntry.TransactionType.Id.SubsystemCode == "A"
&& registerEntry.Receipt != null
&& registerEntry.PostDate != null
&& XXX >= lastInvoicedDate
select registerEntry.TransactionAmount.GetValueOrDefault(0)).Sum();
Value XXX =
if registerEntry.AddedDate = registerEntry.PostDate
registerEntry.AddedDate
else
registerEntry.PostDate
The values registerEntry.AddedDate and registerEntry.PostDate are DateTime type, however when comparing I need to only compare the date and not the time
Does anyone have any idea on how to do this?
Why not just use registerEntry.PostDate for XXX? If it is equal to registerEntry.AddedDate, might as well use it and simplify the whole thing
var paymentReceivedAmt = (from registerEntry in RegisterEntries
where registerEntry.TransactionType != null
&& registerEntry.TransactionType.Id.SubsystemCode == "A"
&& registerEntry.Receipt != null
&& registerEntry.PostDate != null
&& registerEntry.PostDate >= lastInvoicedDate
select registerEntry.TransactionAmount.GetValueOrDefault(0)).Sum();
Depending on the LINQ provider you're using you can try the ternary operator:
var paymentReceivedAmt = (from registerEntry in RegisterEntries
where registerEntry.TransactionType != null
&& registerEntry.TransactionType.Id.SubsystemCode == "A"
&& registerEntry.Receipt != null
&& registerEntry.PostDate != null
&& (registerEntry.AddedDate == registerEntry.PostDate
? registerEntry.AddedDate
: registerEntry.PostDate) >= lastInvoicedDate
select registerEntry.TransactionAmount.GetValueOrDefault(0)).Sum();

Null value in linq where clause

I'm having an issue where I want to return results where something matches and I get an error if one of the properties I'm trying to match is null.
if (!string.IsNullOrEmpty(searchString))
{
Infos = Infos.Where(
x =>
x.FirstName.ToLower().Contains(searchString) ||
x.LastName.ToLower().Contains(searchString) ||
x.ContractNum.ToLower().Contains(searchString) ||
x.VIN.ToLower().Contains(searchString) ||
x.Claim.InitiatedBy.ToLower().Contains(searchString)
).ToList();
}
If ContractNum or VIN, for example, are null then it throws an error. I'm not sure how to check if one of these are null inside of a linq query.
You can add explicit null checks:
Infos = Infos.Where(
x =>
(x.FirstName != null && x.FirstName.ToLower().Contains(searchString)) ||
(x.LastName != null && x.LastName.ToLower().Contains(searchString)) ||
(x.ContractNum != null && x.ContractNum.ToLower().Contains(searchString)) ||
(x.VIN != null && x.VIN.ToLower().Contains(searchString)) ||
(x.Claim != null && x.Claim.InitiatedBy != null && x.Claim.InitiatedBy.ToLower().Contains(searchString))
).ToList();
You have multiple options, first is to do an explicit check against null and the other option is to use Null propagation operator.
x.FirstName != null && x.FirstName.ToLower().Contains(searchString)
or
x.FirstName?.ToLower()?.Contains(searchString) == true
But I would suggest you to use IndexOf instead of Contains for case
insensitive comparison.
something like:
x.FirstName?.IndexOf(searchString, StringComparison.CurrentCultureIgnoreCase) >= 0)
Checking the property is null or empty before comparing it it's the only way I know
if (!string.IsNullOrEmpty(searchString))
{
Infos = Infos.Where(
x =>
(!String.IsNullOrEmpty(x.FirstName) && x.FirstName.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.LastName) && x.LastName.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.ContractNum) && x.ContractNum.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.VIN) && x.VIN.ToLowerInvariant().Contains(searchString)) ||
(x.Claim != null && !String.IsNullOrEmpty(x.Claim.InitiatedBy) && x.Claim.InitiatedBy.ToLowerInvariant().Contains(searchString))
).ToList();
}
EXTRA: I added a check on the Claim property to make sure it's not null when looking at InitiatedBy
EXTRA 2: Using the build in function IsNullOrEmpty to compare string to "" and nullso the code is clearer.
Extra 3: Used of ToLowerInvariant (https://msdn.microsoft.com/en-us/library/system.string.tolowerinvariant(v=vs.110).aspx) so the lowering action will act the same no matter of the culture.
You could use ?? to replace it with a acceptable value.
(x.ContractNum??"").ToLower()
I would use the null conditional operator ?, this will however, return a nullable bool? so you will need to handle that appropriately.
Some examples on how to do this:
x?.FirstName?.ToLower().Contains(searchString) == true;
x?.FirstName?.ToLower().Contains(searchString) ?? false;
An alternative method to keep the comparison logic in one place to use a sub collection of the properties and check on those:
Infos = Infos.Where(i=>
new[] {i.FirstName,i.LastName,i.ContractNum /*etc*/}
.Any(w=> w?.ToLower().Contains(searchString) ?? false))
.ToList();
(It does read out all properties, but that shouldn't cost much performance and gains much maintainability )

Eliminating Redundant NULL checks in Entity

List<OfferDTO> offers = dbContext.Offer.Where(x => x.IsDeleted)
.OrderBy(i => i.OfferID)
.Skip(start).Take((length))
.Select(y => new OfferDTO
{
OfferStageValue = y.OfferStage.Value ?? null,
PropertyAddressLine1 = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.AddressLine1 : string.Empty,
PropertyAddressLine2 = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.AddressLine2 : string.Empty,
PropertyCity = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.City : string.Empty,
PropertyZip = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.PostalCode : string.Empty,
})
.ToList();
In the above example, I am casting directly to an object with a select statement.
Certain properties need to be checked for a null value before their values are used in the new object.
However, as you see, different properties check the same value for null over and over again. Specifically here, you see:
(y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null)
is checked a few times in a row.
In the ensuing SQL, you are seeing that the query is checking for null each time:
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent13].[AddressID] IS NOT NULL)) THEN [Extent14].[AddressLine1] ELSE #p__linq__1 END AS [C5],
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent15].[AddressID] IS NOT NULL)) THEN [Extent16].[AddressLine2] ELSE #p__linq__2 END AS [C6],
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent17].[AddressID] IS NOT NULL)) THEN [Extent18].[City] ELSE #p__linq__3 END AS [C7]
Is there a way to check once and carry that over, or is this the best way to safely use these values?
NOTE
y is the base table
PropertyAuction,
Property, and
Address are all separate tables as well and may not contain data.
You can get a bit more elegant in the LINQ itself by switching to Query syntax, and using the let keyword, but I think you're going to find your generated SQL is (has to be) pretty similar:
var offers = (from o in dbContext.Offers
where o.IsDeleted
let p = o.PropertyAuction.Property
let a = p != null ? p.Address : null
orderby o.OfferID
select new OfferDTO
{
OfferStageValue = o.OfferStage.Value,
PropertyAddressLine1 = a != null ? a.AddressLine1 : string.Empty,
PropertyAddressLine2 = a != null ? a.AddressLine2 : string.Empty,
PropertyCity = a != null ? a.City : string.Empty,
PropertyZip = a != null ? a.PostalCode : string.Empty,
})
.Skip(start).Take(length)
.ToList();
EDIT
Just got a chance to put the full model into the compiler and check the SQL - as expected, the generated SQL is identical to the SQL generated from your original query; (i.e., even though extracted into a single check in the LINQ, the generated SQL inlines these checks four times, resulting in identical SQL code).
If your primary goal was cleaner LINQ code, at least this answer accomplishes that.

Using ternary operation without else in linq?

I need to know how do i use ternary operator without else. In the example I need to check 2 criterias (cityId != null) && (cityId != 0). I cannot use normal if conditions. So if it doesn't happen i want to list all titles. I don't want to show else condition is x.ProvinceId == 15
public JsonResult mt(int? cityId)
{
var getCities = locationRepository.Get(null).Where(x => ( (cityId != null) && (cityId != 0) ? x.ProvinceId == cityId : x.ProvinceId == 15 )).Select(x=>x.Title);
return Json(new { items = getCities }, JsonRequestBehavior.AllowGet);
}
The conditional operator is a ternary operator, meaning it accepts three operands. So omitting one is like omitting the second operand of an addition.
However you can simply reformat it as one condition
Depending on what should actually happen if the condition is not met.
This will accept all that does not meet the condition you have
x => (cityId ?? 0) == 0 || x.ProvinceId == cityId

Categories

Resources