Create a search method with AND/OR options in Linq and C# - 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);
}

Related

predicate return null while it should return list of values C#

Good Day To you all
I have issue with using 'predicate'
I'm trying to make function to return the results of some search text
here is the body of the function
public List<SaddadVM> GetAllBills(int Page = 1, int Take = 10, int? SearchField = null, string SearchItem = null)
{
var predicate = PredicateBuilder.New<SaddadVM>();
if (!string.IsNullOrWhiteSpace(SearchItem))
{
predicate = predicate.And(x => x.BillAcct == SearchItem);
}
predicate = predicate.And(x => x.Id != 0);
var bill = (from d in _context.Saddad
join R in _context.Requests on d.ReqId equals R.Id
join l in _context.Documenter on R.DocumenterId equals l.Id
where d.RecordStatus != GeneralEnums.RecordStatus.Deleted
select new SaddadVM
{
BillAcct = d.BillAcct,
ReqID = d.ReqId,
DocName = l.FullName,
DueDt = d.DueDt,
BillAmount = d.BillAmount
}).Where(predicate).Skip((Page - 1) * Take).Take(Take).ToList();
return bill;
}
Knowing that the the Bill object should return at least 2 depending on running the same query on DB as follows :
enter image description here

How to add serial no in IQueryable query in LinQ?

This is my method that returns IQueryable query. I want to generate serial no based on number of records.
public IQueryable<CompanyModel> GetCompanyData()
{
var query = (from e in Context.tblCompany
where e.Cmp_Id == this.CompanyId
&& e.TenantId == this.TenantId
select new CompanyModel()
{
CmpId = e.Cmp_Id,
SrNo = 0,
});
return query;
}
You can try below code may it help you
public IQueryable<CompanyModel> GetCompanyData()
{
List<CompanyModel> query = (from e in Context.tblCompany
where e.Cmp_Id == this.CompanyId
&& e.TenantId == this.TenantId
select new CompanyModel()
{
CmpId = e.Cmp_Id,
SrNo = 0,
}).ToList();
int counter=0;
query.Foreach(x=>x.SrNo = counter++);
return query;
}
As far as I am aware, the only way to have sequence number generated by the query itself is using "Select" method overload with 2 parameters, however it doesn't work with IQueryable, so your query will look like:
Context.tblCompany.Where(e => e.Cmp_Id == this.CompanyId && e.TenantId == this.TenantId)
.Select(e => new { CmpId = e.Cmp_Id })
.AsEnumerable()
.Select((e, i) => new CompanyModel { CmpId = e.CmpId, SrNo = i });
Obviously, this sequence number will be generated on the client and the resulting expression can't be treated as IQueryable anymore. At the same time, the part before "AsEnumerable" will be successfully translated into SQL.

Linq query construction based on empty parameters

I have an object like this:
public class Filters
{
List<string> A { get; set; }
List<string> B { get; set; }
}
I will use it as parameters of a function that will execute a query on a Database.
These itens, A and B, will indicate the "where" filters.
Look at the following code:
//Filters Initialization
Filters TestFilters = new Filters();
TestFilters.A = new List<string>();
TestFilters.B = new List<string>(new string[] {"testfilter1","testfilter2"}); //WHERE B == "testfilter1" OR B == "testfilter2"
//Call of the function that executes SELECT on DB
List<Table> Result = TestFunction(TestFilter);
The function:
public static List<Table> TestFunction(Filters pParameter)
{
ExampleDataContext dc = new ExampleDataContext(Properties.Settings.Default.ExampleConnectionString);
List<Table> SelectResult = new List<Table>();
if (pParameter.A.count != 0 && pParameter.B.count != 0)
SelectResult = (from x in dc.Table
where pParameter.A.Contains(x.A)
where pParameter.B.Contains(x.B)
select x).ToList();
else if (pParameter.A.count == 0 && pParameter.B.count != 0)
{
SelectResult = (from x in dc.Table
where pParameter.B.Contains(x.B)
select x).ToList();
}
else if (pParameter.A.count != 0 && pParameter.B.count == 0)
{
SelectResult = (from x in dc.Table
where pParameter.A.Contains(x.A)
select x).ToList();
}
else if (pParameter.A.count == 0 && pParameter.B.count == 0)
{
SelectResult = (from x in dc.Table
select x).ToList();
}
return SelectResult;
}
Sometimes A or/and B is/are empty, then I am using "IF" structure to handle it.
Maybe I could face a problem where my filter is bigger, more than 2 parameters, and it would be difficult/boring to code.
My question is: It is working, but is there another way to do it, instead of using IF?
Please have a try(chained linq), I just rewrite by hand and did not compile or run:
public static List<Table> TestFunction(Filters pParameter)
{
ExampleDataContext dc = new ExampleDataContext(Properties.Settings.Default.ExampleConnectionString);
var SelectResult = dc.Table;
if (pParameter.A.count != 0)
SelectResult = from x in SelectResult
where pParameter.A.Contains(x.A)
select x;
if (pParameter.B.count != 0)
{
SelectResult = from x in SelectResult
where pParameter.B.Contains(x.B)
select x;
}
return SelectResult.ToList();
}
You can do something like this:
SelectResult = (from x in dc.Table
where pParameter.A.Any() && pParameter.A.Contains(x.A)
where pParameter.B.Any() && pParameter.B.Contains(x.B)
select x).ToList();
The code is straightforward. Just use correct LINQ syntax.
//Parameters class
public Class Parameters
{
public List<string> A {get; set;}
public List<int> B {get; set;}
}
//some function in a controller
public List<SomeResult> GetResult(Parameters pars)
{
var db = new DbContext();
var result = db.SomeResult.Where(s => s.Any(p =>p.SomeString == pars.A
|| p.SomeInt == pars.B))
.ToList();
return result;
}
with IQueryable<Table> (type of dc.Table and dc.Table.Where()), you can chain criteria, and export ToList only in buttom line.
so if yoy change the SelectResult from List<Table> to IQueryable<Table>,
each criteria can be chained to the previous expression, and it is done as though AND:
public static List<Table> TestFunction(Filters pParameter)
{
ExampleDataContext dc = new ExampleDataContext(Properties.Settings.Default.ExampleConnectionString);
//all
IQueryable<Table> SelectResult = dc.Table;
//add conditions
if (pParameter.A.count != 0 )
SelectResult = SelectResult.Where(x => pParameter.A.Contains(x.B));
if (pParameter.B.count != 0)
SelectResult = SelectResult.Where(x => pParameter.A.Contains(x.B));
//export, with one\two\zero conditions
return SelectResult.ToList();
}
If your case is more complex (such as multiple conditions and with OR operators) consider using the wonderful tool PredicateBuilder.
I have tried some of the options, but unsucessfully.
Below you can find the code that worked for me:
List<Table> SelectResult = (from x in dc.Table
where (pParameter.A.Count != 0 ? pParameter.A.Contains(x.A) : true)
where (pParameter.B.Count != 0 ? pParameter.B.Contains(x.B) : true)
select s).ToList();

how to create dynamic linq query based on search criterias

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.

Building the 'where' clause in a Linq query

In this query, I always want the 'normal' type element.
If the _includeX flag is set, I want the 'workspace' type elements, too.
Is there a way to write this as one query? Or build the where clause based on _includeX before submitting the query?
if (_includeX) {
query = from xElem in doc.Descendants(_xString)
let typeAttributeValue = xElem.Attribute(_typeAttributeName).Value
where typeAttributeValue == _sWorkspace ||
typeAttributeValue == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
}
else {
query = from xElem in doc.Descendants(_xString)
where xElem.Attribute(_typeAttributeName).Value == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
}
You can break it into a separate predicate:
Predicate<string> selector = x=> _includeX
? x == _sWorkspace || x == _sNormal
: x == _sNormal;
query = from xElem in doc.Descendants(_xString)
where selector(xElem.Attribute(_typeAttributeName).Value)
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
Or inline the condition:
query = from xElem in doc.Descendants(_xString)
let typeAttributeValue = xElem.Attribute(_typeAttributeName).Value
where (typeAttributeValue == _sWorkspace && _includeX) ||
typeAttributeValue == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
Or remove query expression usage and do it this way:-
var all = doc.Descendants(_xString);
var query = all.Where( xElem=> {
var typeAttributeValue = xElem.Attribute(_typeAttributeName).Value;
return typeAttributeValue == _sWorkspace && includeX ) || typeAttributeValue == _sNormal;
})
.Select( xElem =>
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
})
Or combine the first and third and do:
Predicate<string> selector = x=> _includeX
? x == _sWorkspace || x == _sNormal
: x == _sNormal;
query = doc.Descendants(_xString)
.Where(xElem => selector(xElem.Attribute(_typeAttributeName).Value))
.Select(xElem => new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};)
It all depends what's going to work cleanest in your context.
Do yourself a favour and buy (and read!) C# in Depth and it'll all make sense a lot more quickly that learning this stuff bit by bit...

Categories

Resources