Entity Framework Search functionality with Dynamic SQL WHERE clause - c#

hi guys i was using Dynamic SQL for search queries where i used to attach WHERE & AND clause piece by piece and form a statement, i recently came to below alternate for this, and life was amazing
cool alternates of Dynamic WHERE-Clause
Select * From tblEmployees
where EmployeeName = Coalesce(#EmployeeName, EmployeeName) AND
Department = Coalesce(#Department, Department ) AND
Designation = Coalesce(#Designation, Designation) AND
JoiningDate >= Coalesce(#StartDate, JoiningDate) AND
JoiningDate <= Coalesce(#EndDate, JoiningDate) AND
Salary >= Coalesce(#Salary, Salary)
now the issue is since i implemented entity framework i need to achieve same with Linq queries. i have nullable Byte type and nullable boolean which i am currently unable to handle
just like Coalesce my stupid attempt was
&& (s.Floors == deal.Floors.HasValue ? null : s.Floors)
below code not matching any results
[HttpPost]
public ActionResult Results(Deal deal, bool exactMatch)
{
List<Deal> deals;
if (exactMatch)
{
deals = dataBase.Deals.Where(s =>
(s.OwnerName.Contains(deal.OwnerName) || s.OwnerName == null)
&& (s.Rooms == deal.Rooms || s.Rooms == null)
&& (s.BathRooms == deal.BathRooms || s.BathRooms == null)
&& (s.Floors == deal.Floors || s.Floors == null)
&& (s.Builtin == deal.Builtin || s.Builtin == null)
&& (s.Kitchens == deal.Kitchens || s.Kitchens == null)
&& (s.DoubleUnit == deal.DoubleUnit || s.DoubleUnit == null)
&& (s.Corner == deal.Corner || s.Corner == null)
&& (s.Remarks.Contains(deal.Remarks) || s.Remarks == null)
).ToList();
}
else
{
deals = dataBase.Deals.Where(s =>
(s.OwnerName.Contains(deal.OwnerName) || s.OwnerName == null)
|| (s.Rooms == deal.Rooms || s.Rooms == null)
|| (s.BathRooms == deal.BathRooms || s.BathRooms == null)
|| (s.Floors == deal.Floors || s.Floors == null)
|| (s.Builtin == deal.Builtin || s.Builtin == null)
|| (s.Kitchens == deal.Kitchens || s.Kitchens == null)
|| (s.DoubleUnit == deal.DoubleUnit || s.DoubleUnit == null)
|| (s.Corner == deal.Corner || s.Corner == null)
|| (s.Remarks.Contains(deal.Remarks) || s.Remarks == null)
).ToList();
}
return View(deals);
}
table has values like
id Bathroom Floors
1 1 2
2 1 4
3 2 6
4 3 1
i need results which has id 1 & 2
for instance in front end user want to only fill bathroom field with "1" and leave floor field empty

Not really the same. In your query your coalesce is on the parameter then taking the record value as the default if it is null. In your c# lambda you are checking if the parameter is the same as the table value and then checking if the table value is null but that omits the possibility of having a null value in the parameter.
Example
Sql
Department = Coalesce(#Department, Department )
would be
(s.Department == deal.Department || deal.Department == null)
not this which is what you have now
(s.Department == deal.Department || s.Department == null)
Edit
If you wanted to duplicate the COALESCE expression you have now you could write it this way although I am not sure if it would decrease efficiency / performance.
(s.Department == (deal.Department ?? s.Department))

You are testing whether the field in the table equals the 'deal' property or the field is null rather than doing this:
s.Remarks.Contains(deal.Remarks) || deal.Remarks == null
If you do this, it should be the equivalent query.
You can do this cumulatively too. For example with the exact match case you can do:
deals = dataBase.Deals;
if (deal.OwnerName != null)
deals = deals.Where(s => s.OwnerName.Contains(deal.OwnerName));
if (deal.Rooms != null)
deals = deals.Where(s => s.Rooms == deal.Rooms)
That can make the resulting query more efficient. There's a similar way to do this with the non exact match through using unions. I don't know the syntax off hand.

Related

Return the same type for two views from datacontext

I have created two views that return exactly the same columns from the same tables. The only difference between the two views is they filter on different parameters. I have added these into my .dbml file
(Picture of views in dbml) This has then auto generated two classes for these two views.
In my code depending on the value of the property Filter one of the two views is queried, either current or previous. I need these views to be returned as the same type. So that IOrderedQueryable<> items has one return type.
Currently they are returning as either clientOrdersQueryCurrent or clientOrdersQueryPrevious. If I set IOrderedQueryable<> items to either one of these and attempt to cast the other type then this causes a runtime error.
IOrderedQueryable<> items;
bool filterQuery = false;
if (!string.IsNullOrEmpty(OrderNumber) || DateFrom != null || Dateto != null || !string.IsNullOrEmpty(TrackingNumber) || UserId != null)
{
filterQuery = true;
}
if (Filter == "Current")
{
if (filterQuery)
{
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()
&& (string.IsNullOrEmpty(OrderNumber) || o.OrderNumber.Contains(OrderNumber))
&& (DateFrom == null || o.OrderPlaced >= DateFrom)
&& (Dateto == null || o.OrderPlaced <= Dateto)
&& (string.IsNullOrEmpty(TrackingNumber) || o.TrackingReference.Contains(TrackingNumber))
&& (UserId == null || o.UserId == UserId)).OrderByDescending(o => o.OrderPlaced);
}
else
{
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
}
else if (Filter == "Previous")
{
if (filterQuery)
{
items = dataContext.clientOrdersQueryPrevious.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()
&& (string.IsNullOrEmpty(OrderNumber) || o.OrderNumber.Contains(OrderNumber))
&& (DateFrom == null || o.OrderPlaced >= DateFrom)
&& (Dateto == null || o.OrderPlaced <= Dateto)
&& (string.IsNullOrEmpty(TrackingNumber) || o.TrackingReference.Contains(TrackingNumber))
&& (UserId == null || o.UserId == UserId)).OrderByDescending(o => o.OrderPlaced);
}
else
{
items = dataContext.clientOrdersQueryPrevious.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
}
else
{
//Default call - current orders
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
The only thing I can currently think of to resolve this is to create a class and have the query map the result to the class after it returns.
What is the best way to do this?
The ORM I am currently using is NHibernate.
Better to map to some common class
IOrderedQueryable<CommonItem> items;
items = dataContext.clientOrdersQueryCurrents.Select(e => new CommonItem{...});
...
items = dataContext.clientOrdersQueryPrevious.Select(e => new CommonItem{...});
It can be AutoMapper's ProjectTo method
items = dataContext.clientOrdersQueryPrevious.ProjectTo<CommonItem>();

What is the best way to search data on multiple fields [duplicate]

This question already has answers here:
Dynamic where clause (OR) in Linq to Entities
(2 answers)
Closed 5 years ago.
I am working in ASP.NET MVC, I have a scenario where user can select multiple options to get list of doctors, this is how my action looks like.
public JsonResult DoctorsList(int? specialization , int? city, int? area, int? insurance_company, string doctor_name )
Any of these arguments can have some value and any number of them can be null also all of can be null in that I will return all the records.
Now I know a long and complicated way where I can make different combinations of these arguments and check which one is null and which one is not and then write my query based on that.
But is there any other shorter more efficient way?
Right now I am using OR conditions to get records like this
var doctors = db.Doctors.Where(e =>
e.specialization == specialization ||
e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).Area.city_id == city ||
e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).area_id == area ||
e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).ClinicInsuranceCompanies
.Select(sin=>sin.company_id).ToList().Contains(insurance_company) ||
e.first_name == doctor_name ||
e.last_name == doctor_name
)
.Select(s => new
{
doctor_name = s.first_name + " " + s.last_name
}).ToList();
But I want it to work in combinations, For Example Selecting Doctors with specialization_id = 1 and city_id=2 , of other combinations like this. But OR condition will be true if only one condition matches
For your scenario i think this approach might work rather than going around if else conditions. And i think && should be used to filter out exactly but you could use || operator if thats what you want,
var doctors = db.Doctors.Where(e =>
(specialization != null && e.specialization == specialization) &&
(city != null && e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).Area.city_id == city) &&
(area != null && e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).area_id == area) &&
(insurance_company != null && e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).ClinicInsuranceCompanies
.Select(sin => sin.company_id).ToList().Contains(insurance_company)) &&
(doctor_name != "" && e.first_name == doctor_name || e.last_name == doctor_name)
)
.Select(s => new
{
doctor_name = s.first_name + " " + s.last_name
}).ToList();
This is what I was looking for #imanshu15 answer gave me a hint.
var doctors = db.Doctors.Where(e =>
(specialization != null && e.specialization == specialization) || (specialization == null)
).Where(e =>
(city != null && e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).Area.city_id == city) || (city == null)
).Where(e =>
(area != null && e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).area_id == area) || (area == null)
).Where(e =>
(insurance_company != null && e.Clinics.FirstOrDefault(cs => cs.doctor_id == e.doctor_id).ClinicInsuranceCompanies
.Select(sin => sin.company_id).ToList().Contains(insurance_company)) || (insurance_company == null)
).Where(e =>
(doctor_name != null && e.first_name == doctor_name) || (doctor_name == null)
)

query to return all records if parameter is null

My database table is:
----------
| Ad |
----------
|Id
|title
|price
|tags
|brand
|model
I have to search Ad by 6 parameters i.e, by brand, model , tags, title, minPrice and maxPrice. Now if brand is null then it should pick all rows else pick only those rows where brand is equal to userDesiredBrand.
My function is:
public async Task<IHttpActionResult> SearchAds(string brand, string model,string tags,string title, int minPrice, int maxPrice){
if(brand != null && model != null && tags != null && title != null && minPrice != null && maxPrice != null){
var ret = from ad in db.Ads
where ad.brand.Equals(brand) && ad.model.Equals(model) && ad.tags.Equals(tags) && ad.title.Equals(title) && ad.price > minPrice && ad.price < maxPrice
select new{
id = ad.Id,
title = ad.title,
//retrieve other attributes.
}
return OK(ret);
}
if(brand != null && model == null && tags != null && title != null && minPrice != null && maxPrice != null){
var ret = from ad in db.Ads
where ad.brand.Equals(brand) && ad.tags.Equals(tags) && ad.title.Equals(title) && ad.price > minPrice && ad.price < maxPrice
select new{
id = ad.Id,
title = ad.title,
//retrieve other attributes.
}
return OK(ret);
}
if(brand != null && model != null && tags == null && title != null && minPrice != null && maxPrice != null){
var ret = from ad in db.Ads
where ad.brand.Equals(brand) && ad.model.Equals(model) && ad.title.Equals(title) && ad.price > minPrice && ad.price < maxPrice
select new{
id = ad.Id,
title = ad.title,
//retrieve other attributes.
}
return OK(ret);
}
//Do I have to write 6 * 6 if statements or is this achievable in one query?
}
In a plain SQL statement I would use the following constraints to achieve your goal:
SELECT
...
WHERE
(#brand IS NULL OR brand = #brand)
AND
(#model IS NULL OR model = #model)
...
where the #variables are the parameters. Translating this backwards to LINQ might look like:
where (brand == null || ad.brand == brand) &&
(model == null || ad.model == model) && ...
Another way, for education purposes only (because I wouldn't recommend using this in production code for performance reasons), would be to construct your query bit by bit:
var query = (from ad in db.Ads);
if (brand != null)
query = query.Where(ad => ad.brand == brand);
if (model != null)
query = query.Where(ad => ad.model == model);
....
Do I have to write 6 * 6 if statements
Absolutely not. If this logic doesn't work as intended for null values:
where ad.brand.Equals(brand)
Then simply add a check for null to that logic:
where (brand == null || ad.brand.Equals(brand))
Another approach could be to build the query in stages. Something like this:
var ads = db.Ads;
if (!string.IsNullOrEmpty(brand))
ads = ads.Where(ad => ad.brand.Equals(brand));
if (!string.IsNullOrEmpty(tags))
ads = ads.Where(ad => ad.tags.Equals(tags));
// etc.
ads = ads.Select(ad => new {
id = ad.Id,
title = ad.title,
//retrieve other attributes.
});
return OK(ads);
That is, you can chain as many clauses as you like and the actual query won't materialize against the database until later anyway. (Until it's actuall enumerated by something. Which in this case is likely the WebAPI framework itself when preparing the response.)
check for null values in the query
where ((brand == null || ad.model.Eqauls(model)
If you opt for T-SQL, you can write everything in one query (and embed it, if you want)
To start with
DECLARE #brand VARCHAR(max);
SET #brand = 'WhatsUse'
SELECT * FROM Ad
WHERE brand IS NULL
SELECT * FROM Ad
WHERE brand = #brand
Now - since you'll be checking for MIN and MAX values of price, you have to use the temp tables for sorting and then using SELECT ..... WHERE ..... for extracting the rows.
Cheers

Not understanding LINQ query

I am maintaining a project and have come across some code which I can't understand. LINQ query:
var toDraw = from tile in testArr.AsEnumerable()
where tile.Item_Business_Unit != null ?
((tile.Ending_Date >= DateTime.Now || tile.Ending_Date == DateTime.MinValue) &&
((tile.Sales_Code == null) || (tile.Sales_Code.ToString() == customerNumber) ||
(tile.Sales_Code.ToString() == cpg)) && (tile.Unit_Price != 0)) :
((tile.Ending_Date >= DateTime.Now || tile.Ending_Date == DateTime.MinValue) &&
((tile.Sales_Code == null) || (tile.Sales_Code.ToString() == customerNumber) ||
(tile.Sales_Code.ToString() == cpg)) && (tile.Unit_Price != 0))
select tile;
From what I understand, from an array a tile is being selected which has the following criteria:
Ending date can be datetime.now or datetime.minvalue
Sales code can be null or can be equal to customer no or cpg
Unit price should be greater than 0
But I am not understanding why there is a conditional expression after tile.Item_Business_Unit since both of the conditions perform the same thing. So will the item be selected even if it has a null business unit? And does this work different from normal if/else operations?
Any suggestions would be very appreciated.
Are you being thrown by the shortcut notation?
x = (test_case) ? (true_part) : (false_part);
If test_case evaluates to true, you would have
Whereas if test_case evaluates to false, this expression would be evaluated
UPDATE:
As an FYI: The resulting test of both sides of that conditional expression above are equal, so that cryptic code is not even necessary.
You could replace that with this:
var toDraw = from tile in testArr.AsEnumerable()
where
((tile.Ending_Date >= DateTime.Now || tile.Ending_Date == DateTime.MinValue) &&
((tile.Sales_Code == null) || (tile.Sales_Code.ToString() == customerNumber) || (tile.Sales_Code.ToString() == cpg)) &&
(tile.Unit_Price != 0))
select tile;

LINQ to entity Error: "Unable to create a null constant value of type ''System.Int32[]". Only entity types, enumeration types

I am receiving the error listed when executing a linq query on my RosterSummaryData_Subject_Local entity. I cannot seem to figure out what is wrong or a solution.
Unable to create a null constant value of type 'System.Int32[]'. Only
entity types, enumeration types or primitive types are supported in
this context.
My LINQ query on my code first entity context:
var subjLocal = customerContext.RosterSummaryData_Subject_Local.Where(s =>
(s.fkRosterSetID == 0) &&
(statsInfo.TestInstanceIDsList.Contains(s.fkTestInstanceID)) &&
(s.fkTestTypeID == statsInfo.TestTypeID) &&
(statsInfo.SchoolYearIDsList.Contains(s.fkSchoolYearID)) &&
(s.fkRosterTypeID == 1) &&
(s.fkSchoolID == 0) &&
(s.fkDepartmentID == 1) &&
(s.fkCourseID == 1) &&
(s.fkPeriodID == 1) &&
(statsInfo.DemoCatIDsList.Contains(s.fkDemoCommonCategoryID)) &&
(statsInfo.DemoCodeIDsList.Contains(s.fkDemoCommonCodeID)) &&
(statsInfo.TestSubjectIDsList.Contains(s.fkTest_SubjectID)));
It sounds like one of your Int32[] types is null. Try adding a check for that before accessing the .Contains methods:
var subjLocal = customerContext.RosterSummaryData_Subject_Local.Where(s =>
(s.fkRosterSetID == 0) &&
(statsInfo.TestInstanceIDsList != null &&
statsInfo.TestInstanceIDsList.Contains(s.fkTestInstanceID)) &&
(s.fkTestTypeID == statsInfo.TestTypeID) &&
(statsInfo.SchoolYearIDsList != null &&
statsInfo.SchoolYearIDsList.Contains(s.fkSchoolYearID)) &&
(s.fkRosterTypeID == 1) &&
(s.fkSchoolID == 0) &&
(s.fkDepartmentID == 1) &&
(s.fkCourseID == 1) &&
(s.fkPeriodID == 1) &&
(statsInfo.DemoCatIDsList != null &&
statsInfo.DemoCatIDsList.Contains(s.fkDemoCommonCategoryID)) &&
(statsInfo.DemoCodeIDsList != null &&
statsInfo.DemoCodeIDsList.Contains(s.fkDemoCommonCodeID)) &&
(statsInfo.TestSubjectIDsList != null &&
statsInfo.TestSubjectIDsList.Contains(s.fkTest_SubjectID)));
Alternatively, if it is Ok for them to be null (I assume it isn't, but just in case), you can change the above checks to follow this pattern:
(statsInfo.DemoCatIDsList == null ||
statsInfo.DemoCatIDsList.Contains(s.fkDemoCommonCategoryID)) &&

Categories

Resources