I have a method which generates from my query.
It looks like this:
private static string PrepareWhereClause(string[] columns)
{
var query = $"({string.Join(" OR ", columns?.Select(c => $"{c} != null AND {c}.ToLower().Contains(@0)"))})";
return query;
}
This query is used on Where
extension from Linq dynamic like this:
public static IQueryable<T> Search<T>(this IQueryable<T> queryable, string phrase, params string[] columns) =>
queryable?.Where(PrepareWhereClause(columns), phrase.ToLower());
My object that is used in the query is:
public class ResponsibilitiesWebDto
{
public Guid Id { get; set; }
public DictionaryBaseDto ResponsibilityType { get; set; }
public UserForDetailedDto Employee { get; set; }
public SupplierForDetailedDto Supplier { get; set; }
}
On argument for PrepereWhereClause
are field of type string which are inside in ResponsibilityType
, Employee
and Supplier
.
Query that is generated by PrepereWhereClause
is:
(ResponsibilityType.Name != null AND ResponsibilityType.Name.ToLower().Contains(@0) OR Employee.EmployeeId != null AND Employee.EmployeeId.ToLower().Contains(@0) OR Employee.Name != null AND Employee.Name.ToLower().Contains(@0) OR Employee.LastName != null AND Employee.LastName.ToLower().Contains(@0) OR Supplier.AccountNum != null AND Supplier.AccountNum.ToLower().Contains(@0) OR Supplier.Name != null AND Supplier.Name.ToLower().Contains(@0))
All is ok when ALL child elements are not null.
Problem is when some of them are null. I looking to this post on SO Dynamic Linq Core OrderBy Nullable Child Property and on issue on github and I try use but it doesn't work :(
I modified my query to this:
private static string PrepareWhereClause(string[] columns)
{
var query = $"({string.Join(" OR ", columns?.Select(c => $"{c} != null AND {c}.ToLower().Contains(@0)"))})";
var query1 = $"({string.Join(" OR ", columns?.Select(c => "((" + ConvertToNullableNested(c) + $") AND {c}.ToLower().Contains(@0))"))})";
var query2 = $"({string.Join(" OR ", columns?.Select(c => $"np({c} != null AND {c}.ToLower().Contains(@0)"))})";
var query3 = $"({string.Join(" OR ", columns?.Select(c => $"(np({c}) AND {c}.ToLower().Contains(@0))"))})";
var query4 = $"({string.Join(" OR ", columns?.Select(c => $"(np({c}) && {c}.ToLower().Contains(@0))"))})";
return query4;
}
New query now is:
((np(ResponsibilityType.Name) && ResponsibilityType.Name.ToLower().Contains(@0)) OR (np(Employee.EmployeeId) && Employee.EmployeeId.ToLower().Contains(@0)) OR (np(Employee.Name) && Employee.Name.ToLower().Contains(@0)) OR (np(Employee.LastName) && Employee.LastName.ToLower().Contains(@0)) OR (np(Supplier.AccountNum) && Supplier.AccountNum.ToLower().Contains(@0)) OR (np(Supplier.Name) && Supplier.Name.ToLower().Contains(@0)))
But I always get this error:
Operator '&&' incompatible with operand types 'String' and 'Boolean'
I try change && to AND but nothing change. What am I doing wrong?
I change query like @orxanmuv write but I still get an error.
Query for it:
(((np(ResponsibilityType.Name) == null or np(ResponsibilityType.Name)) AND ResponsibilityType.Name.ToLower().Contains(@0)) OR ((np(Employee.EmployeeId) == null or np(Employee.EmployeeId)) AND Employee.EmployeeId.ToLower().Contains(@0)) OR ((np(Employee.Name) == null or np(Employee.Name)) AND Employee.Name.ToLower().Contains(@0)) OR ((np(Employee.LastName) == null or np(Employee.LastName)) AND Employee.LastName.ToLower().Contains(@0)) OR ((np(Supplier.AccountNum) == null or np(Supplier.AccountNum)) AND Supplier.AccountNum.ToLower().Contains(@0)) OR ((np(Supplier.Name) == null or np(Supplier.Name)) AND Supplier.Name.ToLower().Contains(@0)))
Error:
Operator 'or' incompatible with operand types 'Boolean' and 'String'
np method should be correct solution for Order By. For Where clause you have to check nulls in whole property path.
private static string PrepareWhereClause(string[] columns) => $"({string.Join(" OR ", columns?.Select(c => BuildLinqExpressionForNestedObject($"{c} != null AND {c}.ToLower().Contains(@0)", c)))})";
private static string BuildLinqExpressionForNestedObject(string propertyExpression, string propertyName)
{
var propertyPath = propertyName.Split(".");
if (propertyPath.Length > 1)
{
List<string> nullChecks = new List<string>();
for (int i = 0; i < propertyPath.Length - 1; i++)
{
var nullCheck = $"{string.Join(".", propertyPath.Take(i + 1))} != null";
nullChecks.Add(nullCheck);
}
return $"({string.Join(" AND ", nullChecks)} AND {propertyExpression})";
}
return $"({propertyExpression})";
}
It will generate null checks on whole path to the nested property. Regards