Deeper level LINQ query in a lambda expression - linq

I would like to get those employees who have a phone number == "666666" using a LINQ query.
These are the class definitions:
public class Employees
{
public List<Phones> Phones{get;set}
}
public class Phones
{
public string Id{get;set;}
public string Number{get;set;}
}
This is my query (my doubt indicated as ???):
var employees= data.GetEmployees()
.Where(e=> e.Phones ???i need to navigate a level below phones ???)
.Select(e => new Employee()
{
Id=e.Id,
Name=e.Name
});
My problem is I don't know how to go a deeper level in this LINQ expression, because in e=>e... I have access to Phones as an IEnumerable but I would like to navigate to Phone's properties.

The easiest way to do this is to use nested LINQ queries. In this case you should look at the Any method.
var employees= data
.GetEmployees()
.Where(e => e.Phones.Any(p => p.Number == "666666"))
.Select(e => new Employee() {
Id = e.Id,
Name = e.Name
});

The parameter passed to the where method is merely a function that returns true or false for each given element, all methods (including LINQ ones (subject to accessing ref/out params etc)) can still be called within it:
var employees= data.GetEmployees()
.Where(e => e.Phones.Any(p => p.Number == "666666"))
.Select(e => new Employee()
{
Id=e.Id,
Name=e.Name
});

var employees= data.GetEmployees()
.Where(e=> e.Phones.Contains(x=>x.Number == "666666"))
.Select(e => new Employee()
{
Id=e.Id,
Name=e.Name
});

Related

How to make oData case insensitive when query calls AsEnumerable?

Since .NET Core 3 we can no longer use GroupBy without calling AsEnumerable or ToList first.
The problem with calling AsEnumerable is that it makes the oData queries case sensitive. This filter with 'aaaa' does not find 'A' for instance:
https://localhost/myweb.api/api/mymethod?$filter=contains(Name,%20%27aaaa%27)
How can I make this case insensitive again?
this is not production code, just an example:
public IQueryable<Dto> GetDto(
{
return (from a in _context.Names
select new Dto
{
Name = a.Name
})
.AsEnumerable() // this is needed for the group by to work
.GroupBy(x => new { x.Name })
.Select(x => new Dto
{
Name = x.Key.Name
})
.AsQueryable();
}
You can try with same cases by making .tolower() or .toUpper() and apply where condition to filter the records:
var searchText = "A";
return (from a in _context.Names
select new Dto
{
Name = a.Name
})
.Where(x=>x.Name.ToLower().Contains(searchText.ToLower()))
.AsEnumerable()
.GroupBy(x => new { x.Name })
.Select(x => new Dto
{
Name = x.Key.Name
})
.AsQueryable();

NHibernate Hitting database once for each record in query

I'm measuring database calls on a slow site using NHibernate profiler, and have noticed immediately that the following is causing a Select N+1 issue.
I've never used NHibernate so was hoping someone could help me out?
public virtual IQueryable<Employee> Employees()
{
return Session.Query<Employee>();
}
public IList<Employee> GetEmployeesByClientId(int clientId)
{
return Employees()
.Where(e => e.ClientId == clientId && e.Deleted == false)
.ToList();
}
At the point of calling ToList() a select statement is ran for every related record of EmployeeDetail, and I'm not sure why.
public virtual EmployeeDetail EmployeeDetail { get; set; }
You can use Fetch method to force a join sql statement to fill the property, for sample:
return Employees()
.Fetch(x => x.EmployeeDetail)
.Where(e => e.ClientId == clientId && e.Deleted == false)
.ToList();

Linq group by distinct and iterate over it

I want to list all categories of all published posts. But I would like to display categories just once.
The Post class has a prop Posts and a constructor for the Categories prop. It's an string array instead of List and I would like to keep it that way.
public class Post
{
public static List<Post> Posts = LoadPosts();
}
public Post()
{
ID = Guid.NewGuid().ToString();
Categories = new string[0]; //array, no List<string>
}
This is my razor markup
<ul class="categories">
#{var cl = Post.Posts.Where(p => p.IsPublished).Select(c => new List<string>(c.Categories));}
#foreach (var cat in cl.Distinct())
{
<li>#cat</li>
}
</ul>
This gives me as output
System.Collections.Generic.List`1[System.String]
I have done something wrong in my Linq, but I am not experienced enough (or awake) to see my mistake.
What you need is the SelectMany method:
Post.Posts
.Where(p => p.IsPublished) // IEnumerable<Post>
.SelectMany(c => c.Categories) // IEnumerable<string>
.Distinct()
It seems odd, but the real counterpart to the SQL select is not the IEnumerable.Select method, but the IEnumerable.SelectMany, because it can "flatten" the result of the selection, while Select makes a separate collection for each element, resulting in:
Post.Posts
.Where(p => p.IsPublished) // IEnumerable<Post>
.Select(c => c.Categories) // IEnumerable<IEnumerable<string>>
.Distinct() // does nothing, since all inner IEnumerable<string>
// objects are different

EF how to invert an object hierarchy

I have a list of Users in a family var foo = someUser.MyFamily();
Each User has a list of Classes public virtual List<Class> Classes {get; set;}
Each Class has a list of Dates public virtual List<Date> Dates {get; set;}
To display what's on this week for the family, I'd like to create a table
date class people...
class people...
date+ class people
So what I really want is to invert this arrangement and have
A list of relevant Dates,
Each Date having a list of Classes,
Each Class having a list of Users (but only users in the family)
I'm stuck figuring out how to say it. I keep thinking it should go something like this
var foo = Users x Classes x Dates as in a join
.GroupBy(x => x.Date)
.Select(x => new { something })
.GroupBy(x => x.Class)
.Select(x => new { something })
etc...
and end up being able to say
foreach (var d in foo)
{
use d.Date
foreach (var c in d.Classes)
{
use c.Class.ClassTime, c.Class.ClassName
foreach (var u in c.Users)
{
use u.FirstName
Thanks for insight.
I GOT IT !!!!!
Thanks to this article I learned how to use SelectMany to flatten a hierarchy http://blogs.interknowlogy.com/2008/10/10/use-linqs-selectmany-method-to-flatten-collections/;
Once I had it flattened it was straightforward to use GroupBy() to rebuild the object hierarchy from Dates -> Meetings -> Users such that
Each Date has a list of Meetings
Each Meeting has a list of Users
var meetingDates = Model.User.MyFamily(true)
.SelectMany(x => x.ClassesIamIn)
.SelectMany(x => x.Class.ClassMeetings, (c, d) => new { Date = d.Date, Meeting = d, c.User })
.GroupBy(x => x.Date)
.Select(x => new
{
Date = x.Key,
Meetings = x.Select(y => new { Meeting = y.Meeting, User = y.User })
.GroupBy(a => a.Meeting)
.Select(a => new
{
Meeting = a.Key,
Users = a.Select(b => b.User)
.OrderBy(d => d.FirstName)
})
.OrderBy(c => c.Meeting.TimeStart.TimeOfDay)
});
Wow this has been a struggle; wow what a relief. The above article was the key. I had known how to use SelectMany() to drill inside lists. The thing I didn't know was the goofy lambda syntax for creating a thing that includes the parent object in it (parent, child) => new { parent child mix }

How to get list that is the distinct select of other lists (LINQ)?

Sorry about the question, I couldn't build the sentence. Here is what I have,
class Brand{
int ModelId;
string name;
}
class Gallery{
IList<Brand> brands;
...
public BrandList{
get{ return brands; }
}
}
I have a list of Gallery. Like this,
IList<Gallery> galleries;
and each Gallery in galleries have many Brands in it. For example, galleries have 6 Gallery object in it. And each gallery has Brands in it. Like this,
Gallery1.Brandlist => Audi, Ford
Gallery2.BrandList => Mercedes,Volvo
Gallery3.BrandList => Subaru
Gallery4.BrandList => Renault
Gallery5.BrandList => Subaru
Gallery6.BrandList =>
What I am trying to get with LINQ is a list of Brands that are distinct of all above's first brand only(so I won't take Ford and Volvo even they are in the list). A gallery doesn't have to have a Brand in their list. So it might be empty as Gallery6. The Output should be,
{Audi, Mercedes, Subaru, Renault}
I don't know how I can do this with LINQ. I tried SelectMany but all I can do with LINQ is simple (p=>p.Something = (int) something).ToList(). I couldn't figure out how to do it.
Use SelectMany and Distinct:
IEnumerable<string> allUniqueBrands = allGalleries
.SelectMany(g => g.BrandList.Select(b => b.Name)).Distinct();
In query syntax:
IEnumerable<string> allBrands = from gallery in allGalleries
from brand in gallery.BrandList
select brand.Name;
IEnumerable<string> allUniqueBrands = allBrands.Distinct();
Edit: Now i got it, you need only the first brands of each BrandList.
If you want to select the Brand you have to provide a custom IEqualityComparer<Brand> which you can use in Distinct. If you neeed a List<Brand>, just call ToList() at the end.
Here's an IEqualityComparer<Brand> for Distinct (or Union,Intesect,Except etc):
public class BrandComparer : IEqualityComparer<Brand>
{
public bool Equals(Brand x, Brand y)
{
if (x == null || y == null) return false;
return x.Name.Equals(y.Name, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(Brand obj)
{
if (obj == null) return int.MinValue;
return obj.Name.GetHashCode();
}
}
and here's the distinct list of all (first) brands:
List<Brand> uniqueFirstBrands = allGalleries
.Where(g => g.BrandList != null && g.BrandList.Any())
.Select(g => g.BrandList.First())
.Distinct(new BrandComparer())
.ToList();
This should work:
var brands = galleries.Where(x => x.BrandList.Any())
.Select(x => x.BrandList.First().Name)
.Distinct();
If you want the result being a collection of Brand objects instead of strings, you could do this:
var brands = galleries.Where(x => x.BrandList.Any())
.GroupBy(x => x.BrandList.First().Name)
.Select(g => g.First().BrandList.First());

Resources