Dynamic Where clause in Lambda expression - c#

I have some filter parameters in the Controller of an ASP.NET MVC project and I need to create Where clause dynamically according to these parameters. If isActive parameter is true, it will get the records having the StatusId = 1. There are also userName and labId parameters in the method that should be matched in the Where clause.
public ActionResult GetStudents(int labId, string userName, bool isAll)
{
var allRecords = repository.Students;
//If isAll, get all the records having StatusId = 1
var result = allRecords.Where(m => (isAll) || m.StatusId == 1);
//???
}
I use the filter above, but I have no idea what is the most suitable way (conventions) for multiple parameters in order to fetch the result fast. Any idea?
Note: I want to filter for all of three parameters and Where clause should contain all of the combinations according to the parameter's values (also is null or empty).

var predicate = PredicateBuilder.False<Record>();
if(isAll)
predicate = predicate.AND(d => d.StatusId ==1);
predicate = predicate.AND(d => d.labID == labid && d.username = username);
return allRecords.Where(predicate);`
You can use a predicate builder

You can concatenate linq-methods as they all return an IEnumerable<T> and are combined using something like an SQL-And (dependinng on what LINQ-provider you use):
IEnumerable<Student> result = allRecords;
if(labelId.HasValue)
result = result.Where(x => x.LabelId == labelId);
else
result = result.Where(x => x.LabelId == 0); // or whatever your default-behaviour is
if(isAll)
result = result.Where(x => x.StatusId == 1);
else
result = result.Where(x => x.StatusId == 0); // or whateever your default-behaviour is when isAll is false
if(!String.IsNullOrEmpty(userName))
result = result.Where(x => x.Name == userName);
else
result = result.Where(x => x.Name == "Claus"); // or whatever the default-behaviour is when the param isnĀ“t set

Do like this
public ActionResult GetStudents(int labId, string userName, bool isAll)
{
var allRecords = repository.Students;
//If isAll, get all the records having StatusId = 1
if (isAll)
{
var result = allRecords.Where(m => m.StatusId == 1 && m.UserName == userName && m.LabId == labId);
}
else
{
// do else things
}
}

you need something like below
public ActionResult GetStudents(int labId, string userName, bool isAll)
{
var allRecords = repository.Students;
//If isAll, get all the records having StatusId = 1
if (isAll)
{
var result = allRecords.Where(m => m.StatusId == 1
&& m.LabId == labId
&& m.UserName == username);
//or
var result = from record in allRecords
where record != null &&
record.StatusId == 1
&& !string.IsNullOrWhiteSpace(record.UserName)
&& record.UserName.Equals(username)
&& record.Labid = labId
select record;
}
else
{
// do else things
}
}

Related

C# Entity Framework how to dynamically add 'And' , 'or' condition to the query

I have following method which applies AND condition. But I would like to have OR condition based on parameter in filter.
public async Task<IList<PerformanceReportUser>> GetUsersForPerformanceReport(PerformanceReportFilter filter)
{
var query = _context.Set<EntityUser>()
.AsNoTracking()
.AsQueryable();
if(filter != null)
{
if (filter.Modified.HasValue)
{
query = query.Where(q => q.Modified >= filter.Modified);
}
// How can I have dynamically the OR condition based on filter parameter?
if (filter.Created.HasValue)
{
query = query.Where(q => q.Created >= filter.Created);
}
}
var result = query.Select(user => ToDomain(user));
return await result.ToListAsync();
}
Basically in SQL, I am expecting something like below.
If parameter is 'Or':
Select *
From EntityUser
Where Created >= '2021-01-01' Or Modified >= '2022-02-02'
If parameter is 'And'
Select *
From EntityUser
Where Created >= '2021-01-01' And Modified >= '2022-02-02'
If you want OR inside this types of querys, you have to do in same line, something similar to this:
var query = _context.Set<EntityUser>()
.AsNoTracking()
.AsQueryable();
if(filter != null)
{
query = query.Where(q =>
(filter.Modified.HasValue && q.Modified >= filter.Modified)
||
(filter.Created.HasValue && q.Created >= filter.Created)
);
}
EDITED
If you want dinamicaly condition based on filter use something similar to this...
var query = _context.Set<EntityUser>()
.AsNoTracking()
.AsQueryable();
if(filter != null)
{
if(filter.condition == "OR"){
query = query.Where(q =>
(filter.Modified.HasValue && q.Modified >= filter.Modified)
||
(filter.Created.HasValue && q.Created >= filter.Created)
);
}else{
query = query.Where(q =>
(filter.Modified.HasValue && q.Modified >= filter.Modified)
&&
(filter.Created.HasValue && q.Created >= filter.Created)
);
}
}

Filtering data from parameters c#

I'm facing a issue when trying to search my page when trying to filter from 5 parameters.
If the parameters is null then I would want to ignore that search and only return results if there is a value in the parameters.
At the moment i used separate parameters but i need something that caters for all 5 and if it has 2 out of 4 values then it should return based on the filter.:
if (name != null)
{
report = report.Where(asd => asd.name == name).OrderBy(x => x.Id).ToList();
}
else if (Id != null)
{
report = report.Where(x => x.Id == Id).OrderBy(x => x.Id).ToList();
}
So this works with only 1 in mind, how would i join all 4 together so it works together.
This is my full code:
[HttpGet]
public ActionResult Get(string Id = null, string name = null, string status = null, string startDate = null, string endDate = null)
{
List<ReportModel> report = new List<ReportModel>();
report = (from data in _context.ReportData
//where data.Id== Id
//&& data.name== name
//&& data.Status == status
//&& data.Date >= Convert.ToDateTime(startDate) && data.Date < Convert.ToDateTime(endDate)
select new ReportModel
{
Id = data.Id,
Name = data.Plant,
Status = data.Status,
Date = data.Date
}).ToList();
if (name != null)
{
report = report.Where(asd => asd.name == name).OrderBy(x => x.Id).ToList();
}
else if (Id != null)
{
report = report.Where(x => x.Id == Id).OrderBy(x => x.Id).ToList();
}
else if (status != null)
{
report = report.Where(x => x.Status == status).OrderBy(x => x.Id).ToList();
}
return Ok(report);
}
Kindly help.
If you care about performance, do not call ToList() early, because it will load whole table into the memory. IQueryable can be combined to produce desired result.
[HttpGet]
public ActionResult Get(string Id = null, string name = null, string status = null, string startDate = null, string endDate = null)
{
var query = _context.ReportData.AsQueryable();
if (name != null)
{
query = query.Where(asd => asd.name == name);
}
if (Id != null)
{
query = query.Where(x => x.Id == Id);
}
if (status != null)
{
query = query.Where(x => x.Status == status);
}
query = query.OrderBy(x => x.Id);
var report = query
.Select(data => new ReportModel
{
Id = data.Id,
Name = data.Plant,
Status = data.Status,
Date = data.Date
})
.ToList();
return Ok(report);
}
Intead of using if condition, you can add directly the condition in the Where clause
if (name != null)
{
query = query.Where(asd => asd.name == name);
}
// replace by
query = query.Where(asd => asd.name == name || name == null);
when name will be null, the Where condition will always true.

Neglect variable from Query in entity framework where condition C#

public ActionResult sortFilteredItem(string sortByValue,string brand,string category,int price=0 )
{
var sortedData = (dynamic)null;
if (sortByValue != "")
{
if(sortByValue == "ltoh")
{
sortedData = DB.Prouducts.where(x=> x.brandName == brand & x.catName == category & x.price == price).ToList();
}
}
return View(sortedData);
}
how i can neglect if price=0 from query means that it does not make any impact on EF query because if price=0 the query does not returning expected output.Because i have not any record that has 0 price so the query is always returning null.
if(price != 0)
{
sortedData = DB.Prouducts.where(x=> x.brandName == brand & x.catName == category & x.price == price).ToList();
}
else
{
sortedData = DB.Prouducts.where(x=> x.brandName == brand & x.catName == category).ToList();
}
i have tried like this it is working good but that is lengthy process.if i have 4 or 5 more variable that,s optional so it is necessary to check null value first for working.Any recommendation ?
You can use the fllowing logic;
sortedData = DB.Prouducts.where(x=> x.brandName == brand
&& x.catName == category
&& (price == 0 || x.price == price)) //use this approach for every optional param
.ToList();
What you can do is apply filters only if the condition holds. Let's say you need to check catName and price. So:
var query = DB.Prouducts.Where(x=> x.brandName == brand);
if (category != null)
{
query = query.Where(x => x.catName == category);
}
if (price != 0)
{
query = query.Where(x => x.price == price)
}
sortedData = query.ToList();
Obviously you'll need one "if" per filter, but it is much better than considering all possible combinations.

Merge two Ordering Results into one result in linq with C#

I order two types of query. I want to show the result as one. if the first query and second query have count these both are merge and show the results of one. So i have created 3 list like jobs, jobs1, jobs2. I am getting values into jobs1 and jobs2. Then i have assigned using union into jobs3
Code
IQueryable<Job> jobs = _repository.GetJobs();
IQueryable<Job> jobs1 = _repository.GetJobs();
IQueryable<Job> jobs2 = _repository.GetJobs();
List<int> lstId = null;
List<int> lstUpdatedListId = null;
List<int> lstConId=null;
var order = _db.GetOrderDetails().Where(od => od.Masters.Id != null && od.OrderId == od.Master.OrderId && od.Master.Status == true && od.ValidityTill.Value >= currentdate).OrderByDescending(od => od.ValidityTill).Select(ord => ord.Master.Id.Value);
var order1 = _vasRepository.GetOrderDetails().Where(od => od.Masters.ConId != null && od.OrderId == od.Masters.OrderId && od.Masters.PaymentStatus == true && od.ValidityTill.Value >= currentdate).OrderByDescending(od => od.ValidityTill).Select(ord => ord.Masters.ConId.Value);
var updatedVacancyList = _repository.GetJobs().Where(c => c.UpdatedDate != null && updateFresh <= c.UpdatedDate).Select(c => c.Id);
if (order1 .Count() > 0)
{
lstConId = order1.ToList();
Func<IQueryable<Job>, IOrderedQueryable<Job>> orderingFunc = query =>
{
if (order1.Count() > 0)
return query.OrderByDescending(rslt => lstConId.Contains(rslt.Con.Id)).ThenByDescending(rslt=>rslt.CreatedDate);
else
return query.OrderByDescending(rslt => rslt.CreatedDate);
};
jobs1 = orderingFunc(jobs);
}
if (order.Count() > 0)
{
lstId = order.ToList();
lstUpdatedJobsListId = updatedVacancyList.ToList();
Func<IQueryable<Job>, IOrderedQueryable<Job>> orderingFunc = query =>
{
if (order.Count() > 0)
return query.OrderByDescending(rslt => lstId.Contains(rslt.Id)).ThenByDescending(rslt => lstUpdatedJobsListId.Contains(rslt.Id)).ThenByDescending(rslt=>rslt.CreatedDate);
if (updatedVacancyList.Count() > 0)
return query.OrderByDescending(rslt => lstUpdatedJobsListId.Contains(rslt.Id)).ThenByDescending(rslt => rslt.UpdatedDate);
else
return query.OrderByDescending(rslt => rslt.CreatedDate);
};
jobs2 = orderingFunc(jobs);
}
jobs = jobs1.Union(jobs2);
and i am getting an error while run the application as follows,
The text data type cannot be selected as DISTINCT because it is not comparable.
I need help to rectify this issue. I want to order in descending also.
One of your columns in Database is "Text" type. Convert it to varchar(MAX)

How to write Linq depending if a value is provided or not

I am trying to write a LINQ statement with some optional where clauses. This is for a search. The user can select a specific site to search or search against all sites:
var query =
_db.STEWARDSHIP
.OrderBy(r => r.SITE.SITE_NAME)
.Where(r => r.SITE_ID == SiteId)
.Where(r => r.VISIT_TYPE_VAL.VISIT_TYPE_ID == VisitTypeId)
.Select(r => new
{
id = r.STEWARDSHIP_ID,
name = r.SITE.SITE_NAME,
visit_type = r.VISIT_TYPE_VAL.VISIT_TYPE_DESC,
visit_date = r.VISIT_DATE
});
return query;
So when the method gets SiteId = 14, for instance, no problem. However, when it gets SiteId = null, then that where clause should not be considered.
Thanks
Eric
That's easy:
var query = _db.STEWARDSHIP.OrderBy(r => r.SITE.SITE_NAME);
if (SiteId != null)
{
query = query.Where(r => r.SITE_ID == SiteId);
}
query = query.Where(r => r.SITE.SITE_TYPE_VAL.SITE_TYPE_ID == SiteTypeId)
.Select(r => new
{
id = r.STEWARDSHIP_ID,
name = r.SITE.SITE_NAME,
visit_type = r.VISIT_TYPE_VAL.VISIT_TYPE_DESC,
visit_date = r.VISIT_DATE
});
return query;
This works because queries compose nicely - and they really only represent queries; it's only when you try to fetch data from them that the query is actually executed.
Can't you just edit the where clause to something like
.Where(r=>SiteId == null || r.SiteId == SiteId)
you can use where clause in one statement ..like this ..
.Where(r => SiteID == null || r.SITE_ID == SiteID)
I'm stealing a trick from TSQL. Just check for the null value as well.
...
.Where(r => SiteID == null || r.SITE_ID == SiteID)
...
The SQL example is this:
WHERE (SITE_ID = #given OR #given IS NULL) --return matches or all
Though if that value is mutable and you want the value at the time the query was built, try this instead:
var localSiteID = SiteID;
...
.Where(r => localSiteID == null || r.SITE_ID == SiteID)
...

Categories