How to skip conditions in "Where" clause with multiple conditions? - c#

I"m working on a WinfForms/C# software for automotive key management that's has a SQL query thats searchs in the table like that:
DataAdapter = SetAdapter($"SELECT * FROM keylist WHERE MANUFACTOR LIKE #manufactor AND KEYTYPE LIKE #type AND SERVICETYPE LIKE #service AND USER_ID = #_user");
So in the begginig I used to use the query directly in the search function but as the project grew, it ended up leaving it without performance, because the query was called directly on the remote server. So I decided to move everything to a list in C # and do the search with lambdas functions.
This is the function with lambda expression:
public List<Key> SearchFilter(string manufactor, string type, string service)
{
return _keys.Where(key => key.Manufactor == manufactor
&& key.Type == type
&& key.ServiceType == service).ToList();
}
The problem is:
In the SQL syntax, when you leave one or more fields, it automatically ignores and checks for the other, but when I use Where <> in the LINQ for example, it ends up returning null or items that do not satisfy conditions and returning other objects.
When I leave one or two of the parameters null, it returns no value. By the way, if I use || instead of && it returns undesirable values.
Is there a way to check if the condition is null and skip to the next clause and return only the values that were passed?

if I understand correctly, I think I should make several "Where" conditions, one for each condition, that is:
return _keys.Where(key => manufactor != string.Empty ? key.Manufactor == manufactor ? key.Manufactor != string.Empty)
.Where(key => key.Type == type)
.Where(key => key.ServiceType == service)
.ToList();
You can also "exclude" or "include" filters if there is value to filter as I did with "key.Manufactor".

Related

Entity Framework Core : how to add conditional where clause from input filter parameter?

I am not sure if this is possible, but I have a linq to sql statement that takes in input filter parameter which could be null or missing a value like so
public List<MyViewModel> GetRecords(SearchDto? filter)
{
List<MyViewModel> results =
this.dbContext.MyTable
Where(s => s.IsActive == 'Y' &&
filter != null && !stringIsNullOrEmpty(s.deptId) && s.deptId == filter.deptId)
ToList();
}
When I add the conditional filter in the Where clause, my result is 0 count when the filter is null or empty.
I prefer not to do the old fashion way which is after the linq is call, add many lines of code like the following
if (!string.IsNullEmpty(filter.deptId)
{
results = result.Where(s => s.deptId == filter.deptId);
}
I would like to add the conditional Where filter in the linq statement instead of old fashion C# code.
Thanks for any advice
To correctly represent conditionally added filtering like in the "old fashion way" you should use something like this:
.Where(s.IsActive == 'Y'
&& (filter == null
|| (string.IsNullOrEmpty(s.deptId) || s.deptId == filter.deptId))`
So the "optional" condition evaluates to true for your AND clause when filter is null or empty.

LINQ statement using one of two parameters

I'm working on a LINQ statement. I have a table of cities where the records have either a countryId or a stateId. I'd like to just write the one statement and have the where clause check to see which of the two parameters is null and then select on the one that is not.
Here's what I'm working with:
public List<City> Cities(int? countryTypeId, int? stateTypeId)
{
if (countryTypeId == null && stateTypeId == null)
return null;
return _db.City
.Where(x => x.StateTypeId == stateTypeId
&& x.CountryTypeId == countryTypeId)
.OrderBy(x => x.Description)
.ToDTOs();
}
I'm pretty new to LINQ, and I know this code isn't right, just adding it for context.
If the State and Country ids are all >0 you simply can do this, no need to check for null
.Where(x => x.StateTypeId == stateTypeId.GetValueOrDefault()
&& x.CountryTypeId == countryTypeId.GetValueOrDefault())
Else you need to add the condition if those nullable inputs have value or not, as mentioned in the comment
Edit: after seeing some comments, if you are looking for list of cities based on either of the parameters, then you should be using || not && in your where condition
Where(x => (stateTypeId.HasValue && stateTypeId.Value == x.StateTypeId)
|| (countryTypeId.HasValue && countryTypeId.Value == x.CountryTypeId))
Note the order matters, this code will first check if stateTypeId has value and if it has it'll match only the cities with that stateTypeId
_db.City.Where(c => c.CountryTypeId?.Equals(countryTypeId) ?? false
| c.StateTypeId?.Equals(stateTypeId) ?? false);
Using null conditional operators - when a type Id is null use the null coalescing operator to return false and fail the match - otherwise check for equality and return matching.
Note you cannot short circuit the OR operator here!
I'm not sure if this is the case, but if one of the input parameters was always null and the entries were guaranteed to always have one property null, the following would be a cool solution:
_db.City.Where(c => (c.CountryTypeId ?? c.StateTypeId) == (countryTypeId ?? stateTypeId))
My DBA has sufficiently beaten it into my head that ignoring parameters in a query (ex: WHERE Field = #PARAM or #PARAM IS NULL) can result in very bad things. As a result, I would encourage you to conditionally add only the parameters that you absolutely need. Fortunately, given that you are working with just two possible parameters, this is trivial.
Start with the base of your query, and then add to it.
var queryBase = _db.City.OrderBy(x => x.Description);
if (countryTypeId.HasValue)
{
queryBase = queryBase.Where(x => x.CountryTypeId == countryTypeId);
}
if (stateTypeId.HasValue)
{
queryBase = queryBase.Where(x => x.StateTypeId == stateTypeId);
}
return queryBase.ToDTOs(); // or .ToList() for a more universal outcome
Add whatever logic you may need if parameters are mutually exclusive, one supercedes the other, etc.

How to simplify complicated conditional clauses when using LINQ to SQL

Complicated statements with lots of AND/OR compenents are horrible to read and prone to errors - in a normal IF statement I might make use of a method call to simplify the is statement - for example:
if (((user == myUser || user == yourUser) && user != Admin)
&& Something > SomethingElse
&& (thresholdDate > item.itemDate || (item.itemDate == null && item.itemType == itemIsDated))
)
{
DoStuff();
}
I could refactor out the user and date parts to make things easier to read:
if (
UserValid(user)
&& Something > SomethingElse
&& DateIsValid(thresholdDate, item)
)
{
DoStuff();
}
What can I do in a LINQ query to simplifiy the nested IF?
For example if I have something along the lines of:
var someResults = DataManager.Things
.Where(item => (item.UserName == currentUser.UserName
|| item.ParentUsername == currentUser.UserName)
&& (item.ItemType == (int) ItemType.MyType
|| item.ItemType == (int) ItemType.YourType)
&& item.Result == null
&& (
(item.Status == null
&& (item.ItemDate < thresholdDate
|| item.ItemType == (int) ItemType.YourType)
)
||
(item.Status != null &&
item.Status != "Rejected")
)
)
** not actual code - just a simplified and generic example.
I'd like to be able to extract parts of the logic into methods - or in some other way seperate out the AND / OR mess such that it's clear what is going on.
I've tried adding a method to the 'item' to perfrom some of the logic a kind of IsValidType(typeOptions) method - this compiles fine but LINQ complains that it does't recognise the method at runtime.
I could use a property on the item - but then I can't pass any context information (which makes it of limited use)
How do you go about making this kind of query readable?
As Tim mentioned in a comment, you can use methods in most LINQ-to-something implementations.
The result would look like this:
.Where(item => CheckUserName(item)
&& CheckItemType(item)
&& CheckItemResult(item)
&& CheckItemStatus(item));
In case of LINQ to SQL, LINQ to Entities or other remote-executed implementations, you can at least take advantage of query optimization and rewrite all && to separate Where calls, because they are equivalent:
.Where(item => item.UserName == currentUser.UserName
|| item.ParentUsername == currentUser.UserName)
.Where(item => item.ItemType == (int) ItemType.MyType
|| item.ItemType == (int) ItemType.YourType)
.Where(item => item.Result == null)
.Where(item => (item.Status == null
&& (item.ItemDate < thresholdDate
|| item.ItemType == (int) ItemType.YourType))
|| (item.Status != null
&& item.Status != "Rejected"));
The resulting query will join the where clauses into one enumerator.
I assume you can't do it directly in your code. Of course if you had plenty of similar conditions, you could generate the expression for them dynamically. But you can do very little with single big Where condition here not hurting the performance =(.
If you consider it more appripriate, you could split your single big condition to few consequent Where() calls. LINQ tree when converting to SQL will merge conditions and generate single select. DBMS can also optimize the generated SQL for maximum efficiency. So you can in some cases intentionally write not optimized queries for better readability and rely on automatic optimizations. Though be careful with it and check if your specific conditions really gets merged and optimized as you expect.
If you can move this code to SQL stored procedure, you could configure LINQ to SQL to call this procedure when you need. That will simplify the calling code, but move complications to SQL. I suppose you can even extract some of your conditions to SQL functions and make them being called, using in the Where clause. Though to be honest I never used it - just read about possibility. For more details and example look at this article: http://weblogs.asp.net/zeeshanhirani/archive/2008/05/21/table-valued-functions-in-linq-to-sql.aspx
Three very different options:
Use Resharper. It contains a number of code inspections and refactorings to reduce if statements or point out redundant predicates.
In linq-to-sql you can skip most check for null. That is because the expressions are translated into SQL and in SQL there is no null reference exception. A linq (to sql) clause like where a.Name == "x" just yields false when there is no a. In linq-to-objects this would throw a null reference exception.
For comparisons that are done really often you may consider adding computed columns to your database tables. Like a column IsRejected that evaluates Status = 'Rejected'. In linq that would reduce to where !item.IsRejected.

get the names starts with numbers or special characters in linq to sql

I need to get the list of names that starts with special characters or numbers in the linq to sql query for my asp.net mvc(C#) application.
I have tried like this (Which may not be efficient):
public List<User> GetUsersStartingWithNonCharacter()
{
List<User> _dbUsers = this.GetAllUsers();
return _dbUsers.Where(p => ((p.FirstName != null && p.FirstName != string.Empty && !char.IsLetter(p.FirstName.ToLower()[0])) || (p.FirstName == null || p.FirstName == string.Empty))).ToList();
}
public List<Users> GetAllUsers()
{
return (from u in _context.pu_Users
where !u.Is_Deleted
select new User
{
UserId = u.User_Id,
Email = u.Email_Address,
FirstName = u.First_Name,
LastName = u.Last_Name
}).ToList();
}
Can anyone suggest the most efficient way to do this in linq to sql?
How do you know if it isn't already efficient? Use somekind of profiler tool, like SQL Server Profiler if you're using MSSQL, that way you can trace your call against the database and see the actual SQL. Of course you can only debug the code to see the generated SQL but it's easier with a profiler tool and you'll see how long time the query takes.
EDIT: I see one part in how you can make it more efficient:
public List<User> GetUsersStartingWithNonCharacter()
{
List<User> _dbUsers = this.GetAllUsers();
return _dbUsers.Where(p => ((p.FirstName != null && p.FirstName != string.Empty && !char.IsLetter(p.FirstName.ToLower()[0])) || (p.FirstName == null || p.FirstName == string.Empty))).ToList();
}
public IQueryable<Users> GetAllUsers()
{
return from u in _context.pu_Users
where !u.Is_Deleted
select new User
{
UserId = u.User_Id,
Email = u.Email_Address,
FirstName = u.First_Name,
LastName = u.Last_Name
};
}
Changing your GetAllUsersto return IQueryable will delay the query to execute until you've applied your filters. This might affect some other aspects of your design but you should consider it since that change might make your where clause run in the database instead of in the code which will result in less data traffic between your application and database. Again, use a profiler to see the difference :).
I'll use Regular Expression in this scenerio
Here is my sample code
return _dbUsers.Where(p=>p.FirstName!=String.Empty)
. Where(p => Regex.Match(p.Firstname[0].ToString(), "[a-zA-Z]").Success).ToList();
I suspect all rows will be retrieved and filted in your application due to the condition:
char.IsLetter(p.FirstName.ToLower()[0])
(Using a regular expression like suggested in another answer will also pull in all rows, and filter them on the client.)
It is possible to check characters in a string with the PATINDEX function, but it's seems only to be available for LINQ via the Entity framework.
You could write a stored procedure using PATINDEX directly to check for the first character to optimize your query. Sample queries can be found at http://www.databasejournal.com/features/mssql/article.php/3071531/Using-SQL-Servers-CHARINDEX-and-PATINDEX.htm.
Sometimes LINQ to whatever will not yield the most optimized solution, but that's just life. In most cases it will give clearer code, but special cases might require work arounds in order to use special operators of the underlying system.

Dynamic WHERE clause in LINQ

What is the best way to assemble a dynamic WHERE clause to a LINQ statement?
I have several dozen checkboxes on a form and am passing them back as: Dictionary<string, List<string>> (Dictionary<fieldName,List<values>>) to my LINQ query.
public IOrderedQueryable<ProductDetail> GetProductList(string productGroupName, string productTypeName, Dictionary<string,List<string>> filterDictionary)
{
var q = from c in db.ProductDetail
where c.ProductGroupName == productGroupName && c.ProductTypeName == productTypeName
// insert dynamic filter here
orderby c.ProductTypeName
select c;
return q;
}
(source: scottgu.com)
You need something like this? Use the Linq Dynamic Query Library (download includes examples).
Check out ScottGu's blog for more examples.
I have similar scenario where I need to add filters based on the user input and I chain the where clause.
Here is the sample code.
var votes = db.Votes.Where(r => r.SurveyID == surveyId);
if (fromDate != null)
{
votes = votes.Where(r => r.VoteDate.Value >= fromDate);
}
if (toDate != null)
{
votes = votes.Where(r => r.VoteDate.Value <= toDate);
}
votes = votes.Take(LimitRows).OrderByDescending(r => r.VoteDate);
You can also use the PredicateBuilder from LinqKit to chain multiple typesafe lambda expressions using Or or And.
http://www.albahari.com/nutshell/predicatebuilder.aspx
A simple Approach can be if your Columns are of Simple Type like String
public static IEnumerable<MyObject> WhereQuery(IEnumerable<MyObject> source, string columnName, string propertyValue)
{
return source.Where(m => { return m.GetType().GetProperty(columnName).GetValue(m, null).ToString().StartsWith(propertyValue); });
}
It seems much simpler and simpler to use the ternary operator to decide dynamically if a condition is included
List productList = new List();
productList =
db.ProductDetail.Where(p => p.ProductDetailID > 0 //Example prop
&& (String.IsNullOrEmpty(iproductGroupName) ? (true):(p.iproductGroupName.Equals(iproductGroupName)) ) //use ternary operator to make the condition dynamic
&& (ID == 0 ? (true) : (p.ID == IDParam))
).ToList();
I came up with a solution that even I can understand... by using the 'Contains' method you can chain as many WHERE's as you like. If the WHERE is an empty string, it's ignored (or evaluated as a select all). Here is my example of joining 2 tables in LINQ, applying multiple where clauses and populating a model class to be returned to the view. (this is a select all).
public ActionResult Index()
{
string AssetGroupCode = "";
string StatusCode = "";
string SearchString = "";
var mdl = from a in _db.Assets
join t in _db.Tags on a.ASSETID equals t.ASSETID
where a.ASSETGROUPCODE.Contains(AssetGroupCode)
&& a.STATUSCODE.Contains(StatusCode)
&& (
a.PO.Contains(SearchString)
|| a.MODEL.Contains(SearchString)
|| a.USERNAME.Contains(SearchString)
|| a.LOCATION.Contains(SearchString)
|| t.TAGNUMBER.Contains(SearchString)
|| t.SERIALNUMBER.Contains(SearchString)
)
select new AssetListView
{
AssetId = a.ASSETID,
TagId = t.TAGID,
PO = a.PO,
Model = a.MODEL,
UserName = a.USERNAME,
Location = a.LOCATION,
Tag = t.TAGNUMBER,
SerialNum = t.SERIALNUMBER
};
return View(mdl);
}
Just to share my idea for this case.
Another approach by solution is:
public IOrderedQueryable GetProductList(string productGroupName, string productTypeName, Dictionary> filterDictionary)
{
return db.ProductDetail
.where
(
p =>
(
(String.IsNullOrEmpty(productGroupName) || c.ProductGroupName.Contains(productGroupName))
&& (String.IsNullOrEmpty(productTypeName) || c.ProductTypeName.Contains(productTypeName))
// Apply similar logic to filterDictionary parameter here !!!
)
);
}
This approach is very flexible and allow with any parameter to be nullable.
You could use the Any() extension method. The following seems to work for me.
XStreamingElement root = new XStreamingElement("Results",
from el in StreamProductItem(file)
where fieldsToSearch.Any(s => el.Element(s) != null && el.Element(s).Value.Contains(searchTerm))
select fieldsToReturn.Select(r => (r == "product") ? el : el.Element(r))
);
Console.WriteLine(root.ToString());
Where 'fieldsToSearch' and 'fieldsToReturn' are both List objects.
This is the solution I came up with if anyone is interested.
https://kellyschronicles.wordpress.com/2017/12/16/dynamic-predicate-for-a-linq-query/
First we identify the single element type we need to use ( Of TRow As DataRow) and then identify the “source” we are using and tie the identifier to that source ((source As TypedTableBase(Of TRow)). Then we must specify the predicate, or the WHERE clause that is going to be passed (predicate As Func(Of TRow, Boolean)) which will either be returned as true or false. Then we identify how we want the returned information ordered (OrderByField As String). Our function will then return a EnumerableRowCollection(Of TRow), our collection of datarows that have met the conditions of our predicate(EnumerableRowCollection(Of TRow)). This is a basic example. Of course you must make sure your order field doesn’t contain nulls, or have handled that situation properly and make sure your column names (if you are using a strongly typed datasource never mind this, it will rename the columns for you) are standard.
System.Linq.Dynamic might help you build LINQ expressions at runtime.
The dynamic query library relies on a simple expression language for formulating expressions and queries in strings.
It provides you with string-based extension methods that you can pass any string expression into instead of using language operators or type-safe lambda extension methods.
It is simple and easy to use and is particularly useful in scenarios where queries are entirely dynamic, and you want to provide an end-user UI to help build them.
Source: Overview in Dynamic LINQ
The library lets you create LINQ expressions from plain strings, therefore, giving you the possibility to dynamically build a LINQ expression concatenating strings as you require.
Here's an example of what can be achieved:
var resultDynamic = context.Customers
.Where("City == #0 and Age > #1", "Paris", 50)
.ToList();

Categories

Resources