Multiple CheckBox Selection Result using Linq without hardcoding - c#

Illustration of database (actual column and table name is different)
EmployeeeID IsSicked IsRetired IsHoliday
1 false false true
2 true false true
3 true true true
4 false false false
What problem am I having?
I have a checkbox for IsSicked, IsRetired and IsHoliday in my application, which allow user to check multiple selection and submit.
The system will return list of employee from database based on the selection of user. For example, when user tick IsSicked and IsRetired, system will return list of user who is sicked and is retired from database.
Based on below code, I need to write 2^3 = 8 possibilities to get the result.
As the column expand to n columns(exluding employeeID column), I will need to write 2^n if else statement to get the query.
If there are 10 column (10 checkboxes selection), then i need to write 2^10 = 1024 if else statement
It seems to me that this supposed to be quite a common problem faced by developers, unfortunately i can't find a good solution to this online
Expectation of solution
Generic combination of C# code and/or LINQ query that no need hard code the logic as below if(...) else if (...)
var employee = db.Employee.AsQueryable():
if(model.IsSicked == true)
{
employee.Where(z => z.IsSicked == true)
}
else if(model.IsRetired == true)
{
employee.Where(z => z.IsRetired == true)
}
...
...
else if(model.IsSicked == true && model.IsRetired == true)
{
employee.Where(z => z.IsSick == true || z.IsRetired == true)
}
else if (model.IsSicked == true && model.IsRetired == true && model.IsHoliday == true)
{
employee.Where(z => z.IsSick == true || z.IsRetired == true || z.IsHoliday == true)
}

Why not use a enum with [Flags]:
[Flags]
public enum EmployeeStatus
{
None = 0,
Sick = 1,
Retired = 2,
Holiday = 4
}
In your database you can store this as an integer.
Your model becomes similar to
public class Employee
{
public int Id { get; set; }
public EmployeeStatus Status { get; set }
}
then LINQ queries become similar to:
employee.Where(z => z.Status == EmployeeStatus.Sick)
Because of using [Flags] you can do the following for multiple states, e.g. sick and retired:
employee.Where(z => z.Status == (EmpoyeeStatus.Sick | EmployeeStatus.Retired))
Please note that | is the boolean OR which translates in the following:
EmpoyeeStatus.Sick | EmployeeStatus.Retired
= 1 | 2
= b01 | b10
= b11
= 3
so effectively works as "and" in our logic.

You can use library called LINQKit and use its PredicateBuilder to create predicate with dynamic ORs.
var employee = db.Employee.AsQueryable():
var predicate = PredicateBuilder.False<Employee>();
if (model.IsSicked == true)
predicate = predicate.Or(p => p.IsSick == model.IsSick);
if (model.IsRetired == true)
predicate = predicate.Or(p => p.IsRetired == model.IsRetired);
if (model.IsHoliday == true)
predicate = predicate.Or(p => p.IsHoliday == model.IsHoliday);
var result = employee.AsExpandable().Where(c => predicate.Invoke(c)).ToList()

You can build up your Where predicate like this:
var employee = db.Employee.AsQueryable():
if(model.IsSicked == true)
employee = employee.Where(z => z.IsSicked == true)
if(model.IsRetired == true)
employee = employee.Where(z => z.IsRetired == true)
if (model.IsHoliday == true)
employee = employee.Where(z => z.IsHoliday == true)
Alternatively, you could do this:
employee.Where(z => (z.IsSicked || !model.IsSicked) &&
(z.IsRetired || !model.IsRetired) &&
(z.IsHoliday || !model.IsHoliday))

Related

LINQ Query with method syntax

My requirement is to make boolean value (IsPC=true) only if I found any value with IsCurrent = true from the list and second condition is to filter the list with G or W codes and third condition is to check the PCBNumber length ==15 with only one from the list.
How short can i able to reduce the below query using LINQ method syntax
below is my query
var CurrentQ= p.List.Where(x => x.IsConCurrent== true);
if (CurrentQ.Count() > 0)
{
var NCurrentQwithWorQ = p.List.Where(x => x.Codes == Codes.W|| x.Codes== Codes.Q).Count();
if (NCurrentQwithWorQ != null)
{
var PCBNumber = p.List.Where(x => x.PCBNumber .Length == 15).Count();
if (PCBNumber == 1)
{
isPC = true;
}
}
}
You can use all conditions in same query like below,
var PCBNumber= p.List.Where(x => x.IsConCurrent== true && (x.Codes == Codes.W|| x.Codes== Codes.Q) && x.PCBNumber.Length == 15);
if (PCBNumber !=null && PCBNumber.Count() == 1)
{
isPC = true;
}
I'm not trying to debug what you wrote, but isn't this really what you're looking for--that is, daisy-chaining your Where conditions?
var isPC = p.List.Where(x => x.IsConCurrent == true).Where(x => x.Codes == Codes.W || x.Codes == Codes.Q).Where(x => x.PCBNumber.Length == 15).Count() == 1;
Both solutions suggested above are correct.
p.List.Where(x => x.IsConCurrent== true && (x.Codes == Codes.W|| x.Codes== Codes.Q) && x.PCBNumber.Length == 15);
p.List.Where(x => x.IsConCurrent == true).Where(x => x.Codes == Codes.W || x.Codes == Codes.Q).Where(x => x.PCBNumber.Length == 15).Count()
Actually they are performed in the same way. The Where function does not force immediate iteration through the data source. Only when you execute the Count function, LINQ will process row by row and execute criterion by criterion to find out which values should be calculated.
I can only suggest you add the Take(2) operator after the where clause. In this case LINQ will stop after finding the first two rows that matches provided criterion and other rows will not be processed.
p.List.Where(x => x.IsConCurrent == true)
.Where(x => x.Codes == Codes.W || x.Codes == Codes.Q)
.Where(x => x.PCBNumber.Length == 15)
.Take(2).Count()

LINQ with Contains return zero rows if i am passing empty string

i don't know how to handle this in LINQ
simply i have a searchKey in which i am passing user enter data and it return with rows. but if i am not passing any searchkey it not given any data. i dont want to add contains if searchkey is empty :(
var AppointmentList = (from app in Con.ios_Appointment
where (app.IS_DELETED == false && app.CLINICIANID == appReq.id
&& app.FNAME.Contains(appReq.searchKey.Trim()) || app.LNAME.Contains(appReq.searchKey.Trim()) || app.ADDRESS.Contains(appReq.searchKey.Trim())
)
orderby app.DATE descending
select new
{
app.ID,
app.FNAME,
app.LNAME,
app.DATE,
app.LONGITUDE,
app.LATITUDE,
app.ADDRESS,
app.STATUS,
app.START_TIME
}).Skip(skipRecord).Take(Convert.ToInt32(record)).ToList();
I suggest you use method syntax to easily build the query up programatically:
var query = Con.ios_Appointment.Where(app => !app.IS_DELETED && app.CLINICIANID == appReq.id);
var search = appReq.searchKey.Trim();
if (search != "")
{
query = query.Where(app => app.FNAME.Contains(search) ||
app.LNAME.Contains(search) ||
app.ADDRESS.Contains(search));
}
var appointments = query
.OrderByDescending(app => app.DATE)
.Select(app => new
{
app.ID,
app.FNAME,
app.LNAME,
app.DATE,
app.LONGITUDE,
app.LATITUDE,
app.ADDRESS,
app.STATUS,
app.START_TIME
})
.Skip(skipRecord)
.Take(Convert.ToInt32(record))
.ToList();
You need to use string.IsNullOrWhiteSpace method:
where (app.IS_DELETED == false &&
app.CLINICIANID == appReq.id &&
(string.IsNullOrWhiteSpace(appReq.searchKey) ||
app.FNAME.Contains(appReq.searchKey.is Trim()) || ...

Dynamic LINQ enumerable select

I have the following bool Variables:
public bool bCompanyID { get; set; }
public bool bTaxCode { get; set; }
public bool bAccountCode { get; set; }
Depending on which ones are set to true I set the LINQ expression (this is an example where all three are true:
System.Collections.Generic.IEnumerable<IP_DataRow> items = null;
items = _vm.Result_Error.Where(n => n.CompanyID == SelectedItem.CompanyID && n.TaxCode == SelectedItem.TaxCode && n.AccountCode == SelectedItem.AccountCode);
What I would like to do (rather than using multiple if statements) is to create this expression dynamically based on the user input.
Is something like this possible, or I simply have to use if statements?
EDIT:
Here is what came to my mind:
items = _vm.Result_Error.Where(n => ((bCompanyID) ? (n.CompanyID == SelectedItem.CompanyID) : true) &&
((bTaxCode) ? (n.TaxCode == SelectedItem.TaxCode) : true) &&
((bAccountCode) ? (n.AccountCode == SelectedItem.AccountCode) : true));
Can I use the above mentioned expression?
Well you can combine then into one using something like this:
System.Collections.Generic.IEnumerable<IP_DataRow> items = null;
items = _vm.Result_Error.Where(n => (!bCompanyID || n.CompanyID == SelectedItem.CompanyID)
&& (!bTaxCode || n.TaxCode == SelectedItem.TaxCode)
&& (!bAccountCode || n.AccountCode == SelectedItem.AccountCode));
but an if statement might be cleaner, especially since you can attach multiple where clauses to an existing query:
items = _vm.Result_Error.AsEnumerable();
if(bCompanyID)
items = items.Where(n => n.CompanyID == SelectedItem.CompanyID);
if(bTaxCode)
items = items.Where(n => n.TaxCode == SelectedItem.TaxCode);
if(bAccountCode)
items = items.Where(n =>n.AccountCode == SelectedItem.AccountCode);
You can combine some conditions with if statements:
var items = _vm.Result.Errors;
if (bCompanyID)
items = items.Where(n => n.CompanyID == SelectedItem.CompanyID);
if (bTaxCode)
items = items.Where(n => n.TaxCode == SelectedItem.TaxCode);
. . .
You can also create single LINQ expression that (as I think) is not so effective:
var items = from item in _vm.Result.Errors
where !bCompanyID || item.CompanyID == SelectedItem.CompanyID
where !bTaxCode || item.TaxCode == SelectedItem.TaxCode
. . .
select item;
In the second case the lazy evaluating of && and || operators is used, so if bCompanyID is false, then right conditions will not evaluating.

Use Condition In Where Clause In Linq

How Can I use Condition in Where Clause?
user can select section,product and model from list and see the result.
i add an item See All in all the filed,if user select See All,he can see all product in all section.so i want to write
a query that check for value if every property equal -1 dot bring in where condition.
//My Model
struct Model
{
public int SectionCode{get;set;}
public int ProductCode{get;set;}
public int ModelCode{get;set;}
}
var query=DBContext.Model.Where(data=>data.ModelCode==_ModelCode//if ModelCode!=-1
&& ProductCode==_ProductCode//if ProductCode!=-1
&& SectionCode==_SectionCode//if SectionCode!=-1 )
I know that i can write it with some if but i have to check a lot of condition.so i want to know, how can i write if in where Clause?
Just don't add a where clause if you don't need it, i.e:
IQueryable<Model> query = DBContext.Model;
if(_ModelCode != -1)
{
query = query.Where(data=>data.ModelCode==_ModelCode);
}
if(_ProductCode!= -1)
{
query = query.Where(data=>data.ProductCode==_ProductCode);
}
if(_SectionCode!= -1)
{
query = query.Where(data=>data.SectionCode==_SectionCode);
}
Try this :
var query=DBContext.Model.Where(data => (ModelCode == -1 || data.ModelCode == _ModelCode)
&& (ProductCode == -1 || ProductCode == _ProductCode)
&& (SectionCode == -1 || SectionCode == _SectionCode)
You could achieve this using logical operators:
var query = DBContext.Model.Where(data =>
(_ModelCode == -1 || data.ModelCode == _ModelCode)
&& (_ProductCode == -1 || data.ProductCode == _ProductCode)
&& (_SectionCode == -1 || data.SectionCode == _SectionCode))

Is there a simpler way of checking if one enum has a particular value and others some other values

A User in my system can have Email, Mobile or Phone and based on the values passed I am checking some conditions and then setting the ContactDataStatus (which is an enum) for each. I am then checking the ContactDataStatus to determine whether the provided contact details were valid.
The enum has the following definition
public enum ContactDataStatus
{
ExistsButUnverified = 1,
ExisitsAndVerified = 2,
IsValid = 3,
IsUninitialized = 4
}
I wrote the following if conditions to set isValid variable
isValid = false;
if (emailStatus == ContactDataStatus.IsValid &&
(mobileStatus == ContactDataStatus.IsValid ||
mobileStatus == ContactDataStatus.IsUninitialized)
&& (phoneStatus == ContactDataStatus.IsValid ||
phoneStatus == ContactDataStatus.IsUninitialized))
{
isValid = true;
}
else if (mobileStatus == ContactDataStatus.IsValid &&
(emailStatus == ContactDataStatus.IsValid ||
emailStatus == ContactDataStatus.IsUninitialized) &&
(phoneStatus == ContactDataStatus.IsValid ||
phoneStatus == ContactDataStatus.IsUninitialized))
{
isValid = true;
}
else if (phoneStatus == ContactDataStatus.IsValid &&
(emailStatus == ContactDataStatus.IsValid ||
emailStatus == ContactDataStatus.IsUninitialized) &&
(mobileStatus == ContactDataStatus.IsValid ||
mobileStatus == ContactDataStatus.IsUninitialized))
{
isValid = true;
}
Is there a simpler/shorter way of writing this?
It would help if you told us what the values were for the enum. It sounds like you want at least one of the values to be valid, and all of the values to either be uninitialized or valid. So one way of expressing that would be:
var statuses = new[] { emailStatus, mobileStatus, phoneStatus };
bool valid = statuses.Any(x => x == ContactDataStatus.IsValid) &&
statuses.All(x => x == ContactDataStatus.IsValid ||
x == ContactDataStatus.IsUninitialized);
Or if the status enum is just IsValid, IsUninitialized and (say) IsInvalid, and you knew that the values would actually be in that set, you could write:
var statuses = new[] { emailStatus, mobileStatus, phoneStatus };
bool valid = statuses.Any(x => x == ContactDataStatus.IsValid) &&
statuses.All(x => x != ContactDataStatus.IsInvalid);
Also, I'd suggest that you removed the "is" prefix from each of the enum values - it's just fluff which makes the code harder to read IMO.
var statuses = new[] { emailStatus, mobileStatus, phoneStatus };
bool isValid = statuses
.Any(s => s == ContactDataStatus.IsValid && statuses.Except(new[] { s }).All(o => o == ContactDataStatus.IsValid || o == ContactDataStatus.IsUninitialized));

Categories

Resources