LINQ to Entities - query across relationships and filter - linq

I am having a very difficult time with querying a set of related entities with LINQ and Lambda expressions.
I have four entities that are related as such ...
Vehicles 1:n VehicleTypes n:1 Prices 1:n CustomerTypes
I am trying to obtain a list of Prices for a given Vehicle and CustomerType. For example I would like to obtain all the Prices for a Ford Mustang (VehicleTypeId = 2). In those Prices I would like included the CustomerType (Government, Commercial, Retail) that the Price pertains to.
I thought I might be able to do the following ...
Prices.Include(p => p.VehicleTypes)
.Include(p => p.CustomerTypes)
.Where(p => p.VehicleTypes.Vehicles.Select(v => v.Id == 2)
However I get this error ...
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<bool>' to 'bool'
I cannot seem to create a Where condition where I can filter the Id of the Vehicle to be purchased yet also include the CustomerType in the results.
EDIT: Just wanted to note that I have included using System.Data.Entity so I have access to the type safe Include extension

If you need the prices to that kind of vehicle and an specific customer type, you can filter as follows:
var prices= Prices.Include(p => p.VehicleTypes)
.Include(p => p.CustomerTypes)
.Where(p => p.VehicleTypes.Vehicles.Any(v => v.Id == 2)// With this condition you make sure that Mustang belong to this set of vehicles
&& p.CustomerTypes.Type=="Commercial");
But in case you want to filter the vehicles in the result, you are going to need to project your query to an anonymous type or a DTO:
var query= Prices.Include(p => p.VehicleTypes)
.Include(p => p.CustomerTypes)
.Where(p => p.VehicleTypes.Vehicles.Any(v => v.Id == 2)
&& p.CustomerTypes.Type=="Commercial")
.Select(p=>new {CustomerType=p.CustomerTypes.Type,
Vehicles=p.VehicleTypes.Vehicles.Where(v => v.Id == 2)});

Related

Query on a many to one and then returning the parents

I have entity Person which have ONE entity Car. The cars entities have multiple properties and one of this is color. I want to get all the persons that have a car with all the colors that is selected in a colorlist.
If person had many cars i should be able to write it like
PersonQuery.Include(c => c.Cars).Where(q=>q.Cars.Any(colors=>ListOfColors.Contains(colors.Color)));
and I want to replace Any with Where, but thats is not correct(!?). What do I miss?
If your expectation is to filter the Cars returned with each applicable Person to only the cars that match the filtered colours, then EF6 does not support this. Your query will return Persons and their cars where that person has a matching colour, but it will return all cars for that person.
One side note: when using Linq expressions, the choice of identifier should either just be a placeholder value (I.e. "x") or reflect the items being queried, not what you are querying for. For instance when writing PersonQuery.Where(c => ... you are querying against "Persons" not "Cars", so "p" would be a less confusing identifier than "c". Expanded out we would have .Where(person => person.Cars.Any(car => ...))
In EF Core 5 there is support for filtered includes:
var persons = context.Persons
.Include(p => p.Cars
.Where(c => ListOfColors.Contains(c.Color))
.Where(p => p.Cars.Any(c=>ListOfColors.Contains(c.Color)))
.ToList();
In EF 6, you would be better off selecting the Cars, then you can build the applicable Person list:
var cars = context.Cars
.Include(c => c.Person)
.Where(c => ListOfColors.Contains(c.Color))
.ToList();
var persons = cars.Select(c => c.Person).ToList();
However, a caveat to this approach is that it should only be done with a virgin DbContext, meaning a DbContext that has not loaded any other data. If for any reason the DbContext is already tracking another car of another colour for one of the applicable person records, that car would automatically be associated with the returned Person so you could easily find yourself with the odd car that doesn't match.
If you are running a DbContext scoped to the request and want to be 100% safe, you should consider projecting the results rather than dealing with the entities directly:
var results = context.Persons
.Where(p => p.Cars.Any(c => ListOfColors.Contains(c.Color)))
.Select(p => new PersonViewModel
{
PersonId = p.Id,
Name = p.Name,
MatchingCars = p.Cars
.Where(c => ListOfColors.Contains(c.Color))
.Select(c => new CarViewModel
{
CarId = c.Id,
Brand = c.Brand,
Model = c.Model,
// ...
}).ToList()
}.ToList();
This ensures that the data returned will always only be the matching coloured cars.

Select two lists as one list in ASP.NET Core linq

I am trying to create a query with ASP.NET Core EF Core and Linq that would give me a List of users based on two different lists, something like this:
return await _context.Users
.Include(u => u.PropertyOwners)
.ThenInclude(po => po.Property)
.ThenInclude(p => p.PropertyTenantLeases)
.Include(u => u.PropertyOwners)
.ThenInclude(po => po.Owner)
.Where(u => u.Id == userID)
.Select(u => new List<User>()
{
u.PropertyTenantLeases.Select(ptl => ptl.Tenant).ToList()
u.PropertyOwners.Select(po => po.Owner).ToList()
}).FirstOrDefaultAsync();
The tables that are used in this query are connected in the following way:
Everything is fine with this query except for the Select, with the Select I am trying to achieve that it returns a list of all the tenants in the PropertyTenantLeases table which is a junction table togheter with all the Owners form the PropertyOwners junction table (both Tenant and Owner are IdentityUser classes. When I right this query like this I get the following error:
The best overloeaded Add method 'List<User>.Add(User)' for the collection initializer has some invalid arguments
and also
Argument 1: cannot convert from 'System.Collections.Generic.List<RosyMasterDBManagement.Models.User>' to 'RosyMasterDBManagement.Models.User'
Joining two list is called a union in Linq -- I believe that is what you want:
note: I still can't test this since you gave a picture of the data model instead of the code that would allow me to be certain of how to implement. expect the fields to be named incorrectly etc.
var ownerlist = _context.Users
.Include(u => u.PropertyOwners)
.ThenInclude(po => po.Owner)
.ToList();
var tenantlist = _context.Users
.Include(u => u.PropertyOwners)
.ThenInclude(po => po.Property)
.ThenInclude(p => p.PropertyTenantLeases)
.ThenInclude(po => po.Tenant)
.ToList();
return ownerlist.Union(tenantlist);
I don't believe you need await() since ToList() forces it to not be lazy. But I could be wrong about that.

Nhibernate Fetch/FetchMany/ThenFetch duplicate results

I'm quering my database. The structure looks like below
Country 1..M CountryLocales
1
..
M
Cities 1..M CityLocales
So, each Country has multiple locales, each City has multiple locales and a Country has multiple cities.
I try to retrieve a city from the database. I want to prefetch the Citylocales, the country and the country locales.
To do this I perform this query:
City city = Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.Country)
.ThenFetch(c => c.CountryLocales)
.FetchMany(x => x.CityLocales)
.AsEnumerable()
.FirstOrDefault();
For some reason I now get both duplicate records for the CountryLocales and for the CityLocales (both twice)
How can I fix this?
You should look into the Future method. This allows you to perform many feteches without bumping into this issue. Your current query is returning a Cartesian Product which you don't want. Using the Future method you can perform multiple queries each using one Fetch whose results are then aggregated together thus resulting in the desired result.
It might go something like this:
var result = Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.Country)
.ToFuture();
Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.CountryLocales)
.ToFuture();
Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.CityLocales)
.ToFuture();
// execute query
City city = result.AsEnumerable().FirstOrDefault();
Take a look at this answer for more information: Multiple Fetches in linq to nhibernate

Use Linq to find records with missing related data

I am trying to use Linq to Entities to find the categories where no orders exist.
TABLE Customer TABLE Order
------------------ ----------------
CustId Category OrderId FKCustId
1 2 1 1
2 2
3 3
This is a classic 1 to many Customer/Order relationship. Given this data, only category 3 has no orders associated with it, so I want to generate a result set with category 3 as the only item. This must be a straightforward query to write, but I have not been able to figure it out. I've tried a ton of different angles; here is one that didn't work:
var dtos = ctx.Customers
.GroupBy(c => c.Category)
.Where(c => !c.Any(c2 => !c2.Orders.Any()))
.Select(c => c.Key);
When I tried this, it returned a category that does have orders while not returning the category missing orders.
Thanks in advance for any help!
Roger
Your query is close, but the !Any followed by !Any is throwing off your logic. You want to select categories where all customers have no orders. But your query selects all categories where no customer has no order. I hope that made sense
Try changing your first !Any to All:
var dtos = ctx.Customers
.GroupBy(c => c.Category)
.Where(c => c.All(c2 => !c2.Orders.Any()))
.Select(c => c.Key);
or in query syntax:
var dtos =
from c in Customers
group c by c.Category into g
where g.All(c => !c.Orders.Any())
select g.Key;
Alternatively change the second !Any to Any.
var dtos = ctx.Customers
.GroupBy(c => c.Category)
.Where(c => !c.Any(c2 => c2.Orders.Any()))
.Select(c => c.Key);
or in query syntax:
var dtos =
from c in Customers
group c by c.Category into g
where !g.Any(c => c.Orders.Any())
select g.Key;
You first need to get the customers who do not have any orders and then get the categories. Sort of do your where first and then get categories out

LInq Order By and Order By Desc

I am using "Linq" to filter list of objects and to sort them, like
myList.Where(x => x.Item!= "SF" && x.AdSize == minadSize)
.OrderBy(x => x.ManufacturingDate)
.OrderBy(x=>x.ExpiryDate);
I doubt whether i am doing it right or not that is if i want to "sorting" on multiple fields then is it necessary to use multiple Order By clause cant it be done with single "OrderBy"
Don't use multiple OrderBy calls - use OrderBy followed by ThenBy:
var query = myList.Where(x => x.Item!= "SF" && x.AdSize == minadSize)
.OrderBy(x => x.ManufacturingDate)
.ThenBy(x => x.ExpiryDate); // Could add more ThenBy calls
If you use OrderBy twice, it will reorder the already-ordered-by-date list by expiry-date, whereas I assume you only want to order by expiry date for items with an equal manufacturing date, which is what the above does.
Obviously there's a ThenByDescending method too. For example:
var query = people.OrderBy(x => x.LastName)
.ThenBy(x => x.FirstName)
.ThenByDescending(x => x.Age)
.ThenBy(x => x.SocialSecurity);

Resources