I have an MVC project with two DropDown-boxes where you can choose from two different lists. In the first list you choose an age and in the second list you chose a name.
Separately, they both work fine. If I choose a name, the view returns all the people with the given name and if I choose age, the same thing happens. The problem is when I try to combine the two lists (dropdowns). Here is the code:
else if (!string.IsNullOrEmpty(age) && (string.IsNullOrEmpty(name)))
{
return View(person.Where(d => d.age == ages));
}
else if (string.IsNullOrEmpty(age) && (!string.IsNullOrEmpty(name)))
{
return View(person.Where(c => c.name == names));
}
else if (!string.IsNullOrEmpty(age) && (!string.IsNullOrEmpty(name)))
{
//CODE TO RETURN VIEW MATCHING BOTH CRITERIA
// For example: IF age is 25 and Name is Bob, i´d like to display all
// 25-year olds named BOB...If there is no 25y/old named bob
// return an empty list
}
You can combine .Where() clauses and simplify all of this. Then just build the criteria and have a single return statement. Something like this:
if (!string.IsNullOrEmpty(age))
person = person.Where(d => d.age == ages);
if (!string.IsNullOrEmpty(name))
person = person.Where(c => c.name == names);
return View(person);
That way any supplied criteria is applied.
You can combine conditions with linq where method
if (!string.IsNullOrEmpty(age) && !string.IsNullOrEmpty(name))
{
return View(person.Where(p => p.name == name && p.age == age).ToList());
}
This code returns persons with name and age corresponding to criteria or an empty list if no result found
Related
I have following Linq queries:
var leaders = _db.Context.Person.Where(p => p.PersonGroup.Any(pg => pg.IsActive && !pg.IsDeleted && pg.GroupType == 'leader'))
var staff = _db.Context.Person.Where(p => p.PersonGroup.Any(pg => pg.IsActive && !pg.IsDeleted && pg.GroupType == 'staff'))
How do I save
pg => pg.IsActive && !pg.IsDeleted part to a variable so my query can be simplified to something like
var staff = _db.Context.Person.Where(p => p.PersonGroup.Any(pg => pg.IsActiveAndNotDeleted && pg.GroupType == 'staff'))
Thanks
So you have a sequence of Persons, where every Person has a property PersonGroup. Apparently PersonGroup is a sequence of zero or more similar items.
We don't know what these items are. What we do know, is that each of these items have Boolean properties IsActive and IsDeleted and a property GroupType which gives an indication of the type of the item: is it a leader, or a staff, or maybe something else.
Be aware: GroupType does not say anything about PersonGroup, but about one item in the PersonGroup. You didn't specify that all items in one PersonGroup have the same GroupType. As far as I know, it can be that PersonGroup has two items, one has GroupType leader and one has GroupType staff.
Requirement: Give me all Persons that have at least one item in property PersonGroup that is Active AND not IsDeleted AND has a third condition.
In your example, the third condition is pg.GroupType == leader, or pg.GroupType == staff. But it could be any condition on the type of items that are in PersonGroup.
I don't know the type of items that are in PersonGroup. Let's say they are items of class Item. Please replace this with the actual type of the items that are in PersonGroup.
My advice would be to create an extension method that takes as input an IQueryable<Person> and the third condition, and returns as output the query for all Persons that have at least one Item in property PersonGroup that is Active, not Deleted and that match the third condition.
If you are not familiar with extension methods, read Extension methods demystified
public static IQueryable<Person> WhereAnyActiveGroup(
this IQueryable<Person> persons,
Expression<Func<Item,Boolean>> thirdCondition)
{
return persons.Where(person => person.PersonGroup
.Where(item => item.IsActive && !item.IsDeleted)
.Where(thirdCondition)
.Any());
}
TODO: invent a proper method name.
In words: from the input sequence of Persons, keep only those persons that have at least one Item in property PersonGroup that is Active and not Deleted and that matches the thirdCondition.
Usage:
using (var dbContext = new PersonelContext())
{
var leaders = dbContext.Persons
.WhereAnyActiveGroup(person => person.GroupType == 'leader'))
.ToList();
var staff = dbContext.Persons
.WhereAnyActiveGroup(person => person.GroupType == 'staff'))
.ToList();
}
You can even concatenate this with other LINQ methods:
var result = dbContext.Persons.Where(person => person.City == "New York")
.WheraAnyActiveGroup(person => person.GroupType == 'staff')
.GroupBy(person => person.Name)
.ToList();
I think the simplest route would be some variation on providing a method that returns the result of a where:
private IEnumerable<Person> GetActiveInRole(string role){
return _db.Context.Person.Where(p => p.PersonGroup.Any(pg => pg.IsActive && !pg.IsDeleted && pg.GroupType == role));
}
And then use that and build on it:
var staff = GetActiveInRole("staff");
var s = staff.Where(p => p.Name == "John");
I am very much new to the Linq queries. I have the set of records in the csv which is like below
ProdID,Name,Color,Availability
P01,Product1,Red,Yes
P02,Product2,Blue,Yes
P03,Product1,Yellow,No
P01,Product1,Red,Yes
P04,Product1,Black,Yes
I need to check for the Names of the each product and if its is not the same in all the records then I need to send an error message.I know the below query is used to find the duplicates in the records but not sure how can I modify it check if it all has the same values.
ProductsList.GroupBy(p => p.Name).Where(p => p.Count() > 1).SelectMany(x => x);
var first = myObjects.First();
bool allSame = myObjects.All(x=>x.Name == first.Name);
Enumerable.All() will return true if the lambda returns true for all elements of the collection. In this case we're checking that every object's Name property is equal to the first (and thus that they're all equal to each other; the transitive property is great, innit?). You can one-line this by inlining myObjects.First() but this will slow performance as First() will execute once for each object in the collection. You can also theoretically Skip() the first element as we know it's equal to itself.
if I understand correctly you want to check if product exists in the list
using System.Linq;
private bool ItemExists(string nameOfProduct) {
return ProductsList.Any(p=> p.Name== nameOfProduct);
}
UPD after author comment:
To know all the records that are not having the same name as the first record:
var firstName = ProductsList[0].Name;
var differentNames = ProductsList.Where(p => p.Name != firstName);
Another option (just to have all other names): ProductsList.Select(p => p.Name).Where(n => n != firstName).Distinct()
Old version
So, if there are at least two different names then you should return an error?
LINQ way: return ProductsList.Select(p => p.Name).Distinct().Count() <= 1
More optimizied way:
if (ProductsList.Count == 0)
return true;
var name = ProductsList[0].Name;
for (var i = 1; i < ProductsList.Count; i++)
{
if (ProductsList[i].Name != name)
return false;
}
return true;
I have a form with multiple search criteria that a user can use to search for employee data, e.g. FirstName, LastName, HireDate, Department, etc.
I am using LINQ and am wondering what method could I use to query a collection of Employes given any of of the search criteria, i.e. a user does not have to enter all, but they do have to enter at least one of the search parameters.
So far, while testing my LINQ statement with two search parameters in place, it seems that I have to see if the search parameter is entered or not.
If this is the case, then this can get quite unwieldy for many search parameters.
// only FirstName is entered
if (!string.IsNullOrEmpty(FirstName) && string.IsNullOrEmpty(LastName))
{
var employees = DB.Employees
.Where(emp => emp.FirstName.Contains(fName));
}
// only LastName is entered
else if (string.IsNullOrEmpty(FirstName) && !string.IsNullOrEmpty(LastName))
{
var employees = DB.Employees
.Where(emp => emp.LastName.Contains(lName));
}
// both parameters are entered
else if (!string.IsNullOrEmpty(FirstName) && !string.IsNullOrEmpty(LastName))
{
var employees = DB.Employees
.Where(emp => emp.FirstName.Contains(fName))
.Where(emp => emp.LastName.Contains(lName));
}
FYI, I initially thought that I could just append Where() statements to my LINQ statement with the pertinent search parameters but I noticed that not all records were being returned that should and thus the above logic of if-then statements.
What about something like this:
IQueryable<Employee> employees = DB.Employees;
if (!string.IsNullOrEmpty(FirstName))
{
employees = employees
.Where(emp => emp.FirstName.Contains(fName));
}
if (!string.IsNullOrEmpty(LastName))
{
employees = employees
.Where(emp => emp.Last.Contains(lName));
}
You can write it like this:
var employees = DB.Employees.AsQueryable();
if (!string.IsNullOrEmpty(fName)
employees = employees.Where(emp => emp.FirstName.Contains(fName));
if (!string.IsNullOrEmpty(lName)
employees = employees.Where(emp => emp.LastName.Contains(lName));
I encountered a similar challenge where a user could select 0, 1 or many values for about 10 searchable fields and needed to construct that query at runtime.
I ended up using LINQKit:
http://www.albahari.com/nutshell/linqkit.aspx
In particular I used it's predicate builder, which is described here:
http://www.albahari.com/nutshell/predicatebuilder.aspx
In your example above, you've encompassed the query multiple within if statements.
The alternative is to build the query as you go.
If you were to declare var employees = DB.Employees outside of those if statements (Assuming that it's always relevant), then you could just tack on your where statements within your if statements if they're applicable.
LINQ gives you deferred execution, so you don't have to have the entire expression in a single block (Even though it feels most natural to do so and in many cases you will).
Things get a bit more complicated if you want to mix in OR's with ANDs, but that's where the previously mentioned predicate builder comes in.
Unfortunately I don't have any examples to share, but those links should get you off to a good start.
var resultData = (from data in db.Abc
where !string.IsNullOrEmpty(firstName) ? data.FirstName == firstName : true
&& data.UserType == userTypeValue
&& !string.IsNullOrEmpty(lastName) ? data.LastName == lastName : true
&& !string.IsNullOrEmpty(gender) ? data.Gender == gender : true
&& !string.IsNullOrEmpty(phone) ? data.CellPhone == phone : true
&& !string.IsNullOrEmpty(fax) ? data.Fax == fax : true
&& !string.IsNullOrEmpty(emailAddress) ? data.Email == emailAddress : true
&& !string.IsNullOrEmpty(address1) ? data.Address == address1 : true
select new
{
UserName = data.UserName,
FirstName = data.FirstName,
Address = data.Address,
CellPhone = data.CellPhone,
Fax = data.Fax,
Email = data.Email
}).ToList();
I'm working in MVC3 project. I was browsing for a while and trying several examples but I could not get it working.
I need to get a list of record from OrderForm table whose DeptID are in another list I already have got.
I'm aware that I need to use Contains() replacing IN SQL clause, but every example that I could read are doing this in the same way
.Where(ListOfDepartments.Contains(q.DeptID))
This is my method at the controller, which obviously is not working:
public ActionResult ValidOrders(string installation, string orderpriority, string stockclass, string validity)
{
int instID = System.Convert.ToInt32(installation);
int orderpriorityID = System.Convert.ToInt32(orderpriority);
int stockclassID = System.Convert.ToInt32(stockclass);
string period = validity;
try
{
var departments = dba.Department
.Where (a => a.InstID == instID);
var valid = dba.OrderForm
.Where(q => q.FormType == 3
&& q.FormStatus == 2
&& q.OrderPriority.OrderPriorityID == orderpriorityID
&& q.StockClassID == stockclassID
&& departments.Contains(q.DeptID));
return View(valid.ToList());
}
catch (Exception)
{
return View("Error");
}
}
What I'm doing wrong?
you need a list of int, not Department.
var departments = dba.Department
.Where (a => a.InstID == instID)
.Select(d => d.Id);//Id is a guess, it maybe another property name
//.ToList();
consider the following code, which represents an attempt to implement partial matching. the intended result is a row for any 1 or more fields that match between the query entity and the data store.
so if you supply person.email we want a match against that, if you supply person.email and person.FirstName we should filter the results further, and so on.
var results = from p in db.Persons
where p.CrookBookUserName.Trim().Contains(person.CrookBookUserName.Trim()) ||
p.email.Trim().Contains(person.email.Trim()) ||
p.FirstName.Trim().Contains(person.FirstName.Trim()) ||
p.LastName.Trim().Contains(person.LastName.Trim()) ||
p.phone.Trim().Contains(person.phone.Trim())
select p;
return results;
unfortunately, this code always returns all rows in the db. why, and what should be the fix?
thanks in advance.
Does person have all fields populated? A p.email.Trim().Contains("") from an empty email address will return true for everything.
An extra (!String.IsNullOrEmpty(..)) && before each parameter, while verbose, will fix it if this is the problem.
Edit re comments:
Yeah, a helper method or something could help here... something like
public static bool ContainsIfNotEmptyTrimmed(this string source, string param)
{
return !String.IsNullOrEmpty(param.Trim()) && source.Trim().Contains(param.Trim());
}
where p.CrookBookUserName.ContainsIfNotEmptyTrimmed(person.CrookBookUserName) || ...
You need to check if the user is providing empty strings.
var query = db.Persons;
if (!string.IsNullOrEmpty(person.CrookBookUserName.Trim()) {
query = query.Where(p => p.CrookBookUserName.Trim().Contains(person.CrookBookUserName.Trim()));
}
if (!string.IsNullOrEmpty(person.email.Trim()) {
query = query.Where(p => p.email.Trim().Contains(person.email.Trim()));
}
// etc...
return query;