LINQ query needed using a conditional operand in the WHERE clause - c#

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();

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.

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 )

Linq and choosing a default value if query is empty without checking size

Instead of checking if the original query has any items, can this be simplified at all? I hate doing this type of check and assign a default value after this type of check.
By the way this is an entity framework query, so it needs to be a part of the entire query.
LogoId = (from t in a.Event.Assets
where t.Type == EventAssetType.Logo
select t.AssetId).Any() ? (from t in a.Event.Assets
where t.Type == EventAssetType.Logo
select t.AssetId).FirstOrDefault() : (a.Event.Organization != null && a.Event.Organization.OrganizationAsset != null ? a.Event.Organization.OrganizationAsset.AssetId : 0),
DefaultIfEmpty has an overload that takes a parameter.
LogoId = (from t in a.Event.Assets
where t.Type == EventAssetType.Logo
select t.AssetId)
.DefaultIfEmpty((a.Event.Organization != null && a.Event.Organization.OrganizationAsset != null ? a.Event.Organization.OrganizationAsset.AssetId : 0)).First();
I believe the simpler you can do is
LogoId = from t in a.Event.Assets
where t.Type == EventAssetType.Logo
select t.AssetId).FirstOrDefault();
if (LogoId == 0)
{
LogoId = a.Event.Organization != null && a.Event.Organization.OrganizationAsset != null
? a.Event.Organization.OrganizationAsset.AssetId : 0;
}
Not sure if is much more simple that your code but maybe is more readable.
Use ?? operator:
(from t in a.Event.Assets
where t.Type == EventAssetType.Logo
select new Nullable<int>(t.AssetId)).FirstOrDefault() ?? (a.Event.Organization != null && a.Event.Organization.OrganizationAsset != null ? a.Event.Organization.OrganizationAsset.AssetId : 0)

Using ternary operator to check if null - null reference is not the same as null

I am attempting to use the ternary operator to check if a value is null and return one expression or the other. What I'm experiencing when incorporating this into a LINQ expression is that the Transact-SQL translation of the LINQ expression attempts to do a "column = null" rather than a "column IS NULL". I have reason to believe that this is because I'm doing the following:
mappedColumnName == (myVar == null ? null : myOtherVar)
Since it translates the following to columnName IS NULL in Transact-SQL:
mappedColumnName == null
Does anyone have any experience with this? I'd very much like to get this to work.
The entire LINQ expression:
(from MenuItem in menuContext.Menus
where MenuItem.IsSysAdmin == (ClientID == 1 ? true : false)
&& MenuItem.IsActive == true
&& MenuItem.ParentMenuCode == (ActiveSubMenu==null?null:ActiveMenu)
&& MenuItem.ClientID == (UseClientMenu ? ClientID : 0)
&& MenuItem.EmployeeID == (UseEmployeeMenu ? EmployeeID : 0)
orderby MenuItem.SortOrder, MenuItem.MenuName
select MenuItem);
Why don't you use
mappedColumnName == (myVar == null ? DBNull.Value: myOtherVar)
instead?
I've never actually attempted to use ternary operators in linq although you could write it as:
(
(myVar == null && mappedColumnName == null ) ||
(myVar != null && mappedColumnNmae == myOtherVar)
)
try this
(from MenuItem in menuContext.Menus
where MenuItem.IsSysAdmin == ((ClientID == 1 )? true : false)
&& MenuItem.IsActive == true
&& MenuItem.ParentMenuCode == ( (ActiveSubMenu==null) ?null:ActiveMenu)
&& MenuItem.ClientID == (UseClientMenu ? ClientID : 0)
&& MenuItem.EmployeeID == (UseEmployeeMenu ? EmployeeID : 0)
orderby MenuItem.SortOrder, MenuItem.MenuName
select MenuItem);

null in Linq query

I em using Linq to EF and trying to get FirstOrDefault entitiy from ObjectSet.
The query looks like this :
Notification not = new Notification();
........
//not.SubTypeID = null;
var elem = ent.Notifications.FirstOrDefault(p =>
p.ID == not.ID &&
p.SubTypeID == not.SubTypeID &&
p.Location == not.Location &&
p.TypeID == ns.TypeID
);
Sometimes SubTypeID can be null and in this case nothing is returnced in elem althogh p.SubTypeID and not.SubTypeID are both null.
But the strange thing is that when I run this query :
var elem = ent.Notifications.FirstOrDefault(p =>
p.ID == not.ID &&
p.SubTypeID == null &&
p.Location == not.Location &&
p.TypeID == ns.TypeID
);
everything works as expected and I see data in elem.
So what I am doing wrong and what is the difference between these 2 queries when not.SubTypeID is null.
In C#, the result of null == null is true. But in SQL, which this expression tree is getting compiled into, the result of NULL = NULL is NULL. Example on PostgreSQL:
SELECT 1 WHERE NULL = NULL;
?column?
----------
(0 rows)
You might consider using an expression like:
p.SubTypeID == not.SubTypeID ||
(p.SubTypeID == null && not.SubTypeID == null)
This is kind of a hack, and there may be a better solution than this. But the NULL = NULL test is likely the root of your problem.

Categories

Resources