how to create dynamic linq query based on search criterias - c#

I have a search form which i want to use to search a database for data. The searchbox has 4 checkboxes and 1 textfield. The problem is how do i build the linq query considering i dont know beforehand what textboxes the user will check for filtering the search. What i have so far is:
[HttpPost]
public ActionResult search(string ulv, string bjorn, string jerv, string gaupe)
{
var query = (from o in db.observasjonene select o);
if (ulv != null)
{
query = query.Where(o => o.art == ulv);
}
if (bjorn != null)
{
query = query.Where(o => o.art == bjorn);
}
if (jerv != null)
{
query = query.Where(o => o.art == jerv);
}
if (gaupe != null)
{
query = query.Where(o => o.art == gaupe);
}
IEnumerable ls = query.ToList();
return Json(ls, JsonRequestBehavior.AllowGet);
}
The problem with the "where" clause is that if a condition is true, it overwrites the results from the earlier condition. I guess i need an "or" statement or something..

If I have understood your question correctly, you want to check if art equals to any of provided values. You can combine those values into collection and check if collection contains art value:
var values = new [] { ulv, bjorn, jerv, game }.Where(v => v != null);
var query = from o in db.observasjonene
where values.Contains(o.art)
select o;
EF translates Contains into SQL IN operator.

I'm using two approaches in this case:
Build dynamic query:
var q = DB.Invoices.AsQueryable();
if (isPresented != null)
q = q.Where(iv => iv.IsPresented == isPresented);
if (ID != null)
q = q.Where(iv => iv.ID == ID.Value);
...........................
return from iv in q
orderby iv.DueDate descending
select iv;
Use Union to combine search results:
var q1 = db.FeeInvoice.Where(fi => [QUERY1]));
if (isPresented != null)
{
var q2 = db.FeeInvoice.Where(fi =>[QUERY2]));
q1.Union(q2);
}
if (ID != null)
{
var q3 = db.FeeInvoice.Where(fi =>[QUERY3]);
q1.Union(q3);
}
...........................

You are comparing all the parameters value to single column in the query ie. art (see you have written same column name in each where condition) . I'm not sure why are you doing so? you can simply take single parameter which compare the value like this
public ActionResult search(string value)
{
query = query.Where(o => o.art == value);
}
or if it is by mistake and you want to apply where condition along with multiple column then you can try something like this
query=query.Where(o => (o.art == ulv || ulv == string.Empty) && (o => o.bjorn == bjorn || bjorn=string.empty) && (o.jerv == jerv || jerv == string.Empty) && (o.gaupe == gaupe || gaupe == string.Empty));
Note: I assume your column name as your parameters name.

Related

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()) || ...

Searching in MVC5 using linq

Using this Linq code I get any values corresponding to the search input i.e if I search country = Italy and gender = female I get both employees from Italy and employees who are female but I need it to be more specific.
i.e if I search Country = Italy and Gender = female I need to get female employees from Italy. Please suggest me a Linq code for the same
Also, I have five search inputs (First Name, Last Name, Designation, Country, Gender) so just (&&) only doesn't do the work here!
Here's the code:
List<Employee> Elist = userdb.Employees
.Where(i => i.FirstName == Fn ||
i.LastName == Ln ||
i.Designation == desig ||
i.Country == country ||
i.Gender == gender)
.ToList();
This is a situation where the nature of IQueryable comes in very useful. You can add Where clauses to your query without actually executing anything against the database. The SQL would only be executed when you materialise the data, for example using ToList(). This is called deferred query execution.
So you can write your code like this:
IQueryable<Employee> query = userdb.Employees;
if(!string.IsNullOrEmpty(Fn))
{
query = query.Where(e => e.FirstName == Fn);
}
if(!string.IsNullOrEmpty(Ln))
{
query = query.Where(e => e.LastName == Ln);
}
// etc. etc.
List<Employee> Elist = query.ToList();
Most likely, you are not wanting to include criteria that is not filled in. You would only want to filter by a value if the value exists (or is not null). Use an IQueryable to build your search and then assign it to Elist.
IQueryable<Employee> Query = userdb.Employees;
if (Fn != null) {
Query = Query.Where(i => i.FirstName.Equals(Fn));
}
if (Ln != null) {
Query = Query.Where(i => i.LastName.Equals(Ln));
}
if (desig != null) {
Query = Query.Where(i => i.Designation.Equals(desig));
}
if (country != null) {
Query = Query.Where(i => i.Country.Equals(country));
}
if (gender != null) {
Query = Query.Where(i => i.Gender.Equals(gender));
}
List<Employee> Elist = Query.ToList();
Personally I would use a PredicateBuilder here. A small example, let's say you just have 2 queries:
Expression<Func<Person, bool>> hasFirstName = p1 => p1.FirstName == Fn;
Expression<Func<Person, bool>> hasLastName= p2 => p2.LastName == Ln";
You could build this into a predicate builder like so and keep on expanding using any sort of logic:
var predicate = PredicateBuilder.False<Employee>();
if (!string.IsNullOrEmpty(Fn))
{
predicate = predicate.And(e => e.FirstName == Fn);
}
if (!string.IsNullOrEmpty(Ln))
{
predicate = predicate.And(e => e.FirstName == Ln);
}
var result = userdb.Employees.Where(predicate);
Try this
List<Employee> Elist = userdb.Employees
.Where(i => (Fn == null || i.FirstName == Fn ) &&
(Ln == null || i.LastName == Ln ) &&
(desig == null || i.Designation == desig) &&
(country == null || i.Country == country) &&
(gender == null || i.Gender == gender)
.ToList();

Excluding Condition if Parameter is null

I am fetching data using LINQ and Lambda with 2 conditions using this Query. Is it possible to write this logic without if else condition -
public List<Pallet> GetPallet(string palletID, string locationID)
{
List<Pallet> data = new List<Pallet>();
if (locationID != null)
data = data.Where(x => x.PalletID == palletID && x.LocationID == locationID).ToList();
else
data = data.Where(x => x.PalletID == palletID).ToList();
return data;
}
Sure it is:
public List<Pallet> GetPallet(string palletID, string locationID)
{
List<Pallet> data = new List<Pallet>();
data = data.Where(x => x.PalletID == palletID && (locationID == null || x.LocationID == locationID)).ToList();
return data;
}
This is a way of doing it with correct results:
data = data
.Where(x => x.PalletID == palletID)
.Where(!string.IsNullOrEmpty(locationID)? x.LocationID == locationID : true)
.ToList();
NOTE: The accepted answer will return the wrong result although the syntax is correct. It'll always compares the LocationID even if the locationID is null or empty. Note that, in case of locationID == null we do not want to compare at all (we don't want this: x => x.LocationID == null).

Create a search method with AND/OR options in Linq and C#

I'm trying to create some methode for searching and filtring data in databese using c# and asp.net mvc 4 (linq)
public ActionResult Search_Names_Using_Location(string b,string d, int c=0,int Id=0)
{
ViewBag.Locations = db.Locations.ToList();
var agentlocation = new AgentLocationViewModel();
agentlocation.agents = new List<Agent>();
agentlocation.agents = (from a in db.Agents
where a.LocationId == Id
&& (a.LocationName == b)
&& (a.age > c )
select a).ToList();
return View(agentlocation);
}
The problem is that user can let some texboxes empty, so the value of Id or a or b can be null so the query will get nothing.
Is their any suggestions to do that (i can go with if else but that's hard if i have 7 or 8 strings)?
You can check for null inside query
public ActionResult Search_Names_Using_Location(string b,string d,
int c=0,int Id=0,)
{
ViewBag.Locations = db.Locations.ToList();
var agentlocation = new AgentLocationViewModel();
agentlocation.agents = new List<Agent>();
var noId = string.IsNullOrWhitespace(Id);
var noB = string.IsNullOrWhitespace(b);
agentlocation.agents = (from a in db.Agents
where (noId || a.LocationId == Id)
&& (noB || a.LocationName == b)
&& (a.age > c )
select a).ToList();
return View(agentlocation);
}
If you have AND conditions only you can use
var query = db.Agents;
if (Id != 0)
{
query = query.Where(x => x.LocationId == Id)
}
if (!string.IsNullOrWhitespace(b))
{
query = query.Where(x => x.LocationName == b)
}
...
var result = query.ToList(); // actual DB call
This will remove useless empty conditions, like WHERE (0 = 0 OR LocationId = 0)
In case of OR conditions and combinations you can take a look at PredicateBuilder
So you can use Or and And predicate combinations like this:
IQueryable<Product> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
}

LINQ where condition filtering

String Sex = getSex(); // return M or F
String[] members = getMembers(); // return member codes in array or null
//if members array is null, no filtering for member codes
var query = from tb in MemberTable
where tb.sex.Equals(Sex) &&
(members != null ? members.Contains(tb.membercode) : true)
select tb;
The code doesn't return correct result. It returns all members no matter what members[] is.
Actually the original LINQ is complex so if there are any other possible solutions, I do not want to write the following:
if (members == null){ /*LINQ1*/ }
else { /*LINQ2*/ }
which is not a good coding style.
Any suggestion for solving this problem?
var query = MemberTable.Where(x=>x.sex.Equals(Sex))
if (members != null)
query = query.Where(x=>members.Contains(x.membercode))
//use your query
query.ToList();
OR
var query = from tb in MemberTable
where tb.sex.Equals(Sex) &&
(members == null || members.Contains(tb.membercode))
select tb;
I prefer the first.
Since || short-circuits, you should be able to do this:
var query = from tb in MemberTable
where tb.sex.Equals(Sex) &&
(members == null || members.Contains(tb.membercode))
select tb;
The (members == null || members.Contains(tb.membercode)) subexpression will be true if members is null, so Contains would not be evaluated.
var list = new List<ModelName>();
list = ctx.MemberTable
.Where(c => c.sex==Sex)
.Where(c => c.membercode==true)
.ToList();

Categories

Resources