join table to table related to the parent table - c#

Still new to LINQ so be nice.
This query:
var query = _ODSContext.AllFacilities
.Where(f => f.AllFacilityContacts.Any(c => ProviderContactIds.Contains(c.ContactID) &&
(c.ContactTypeName == "Primary Rep")))
.Where(f => f.TermDate > DateTime.Now)
.Include(a => a.Address)
.Include(b => b.AllFacilityContacts)
.Include(c => c.AllPractitionerLocations)
.Include(e => e.AllFacilityServices)
.OrderBy(f => f.FacilityName);
works fine.
However, I want to add a table that joins to AllpractitionerLocations:
Here's what I tried:
var query = _ODSContext.AllFacilities
.Where(f => f.AllFacilityContacts.Any(c => ProviderContactIds.Contains(c.ContactID) &&
(c.ContactTypeName == "Primary Rep")))
.Where(f => f.TermDate > DateTime.Now)
.Include(a => a.Address)
.Include(b => b.AllFacilityContacts)
.Include(c => c.AllPractitionerLocations)
.Include(d => d.AllPractitionerNetworkSpecialty)
.Include(e => e.AllFacilityServices)
.OrderBy(f => f.FacilityName);
But I get:
'AllFacility' does not contain a definition for
'AllPractitionerNetworkSpecialties' and no accessible extension method
'AllPractitionerNetworkSpecialties' accepting a first argument of type
'AllFacility' could be found (are you missing a using directive or an assembly reference?
Which is technically accurate. AllPractitionerNetworkSpecialties is related to AllPractitionerLocations.
How do I do the join between those two tables in LINQ?
Thanks,

You use ThenInclude
Simplistically, Include starts off from the root entity, ThenInclude carries on from the entity type that you call it on. If you conceive your graph as a hub-spoke type affair with AllFacilities as the hub, your Include starts another spoke from the hub, whereas ThenInclude continues an existing spoke. If a customer has orders and orders have products, you'd context.Customer.Include(... Orders).ThenInclude(... Products). If you wanted to "go back to the hub" and get the Customer>Address>Country>TaxYearCodings you'd
context.Customer
.Include(... Orders)
.ThenInclude(... Products)
.Include(... Address)
.ThenInclude(... Country)
.ThenInclude(... TaxYearCodings)
We (at work) tend to indent another level when we ThenInclude to signify "continuing a spoke", and indent all the Includes the same to signify "going back to the hub".
Possibly also worth pointing out that you can chain your Include and ThenInclude by accessing the chain "in a one-er" but you can't navigate into collections, only single props - you start another inclusion when you hit a collection
customer.Include(c => c.Address.Country.TaxYearCodings).ThenInclude(tyc => ...)
Be careful; the amount of data you load when you start (then)including a lot can be enormous

Related

C# LINQ Filter deep nested list

I've a structure based of list containing other list. I need to filter the list based on the value of a property based in the deepest part of the list.
Right now I'm doing this:
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts);
queryable = queryable
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)));
I join the needed tables, then I filter them based on the ActionTypeId based 3 levels below the top list.
First off all, is it possible to do this in 1 step ( include the filtering with the joins ), second of all, the second part is not working as my list gets empty, but I'm certain that actions with that type get values.
Using .NET Core 2.0.3 btw!
To answer your first part, you can do this
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts)
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)))
To your second part, it should be working, id check your data, as this is pretty straight forward

Entity Framework Take() returns more elements [duplicate]

This question already has answers here:
Entity Framework Include OrderBy random generates duplicate data
(6 answers)
Closed 6 years ago.
Can someone please explain why following query returns list of 8 vessels?
var vessels = await db.Vessels
.Include(m => m.Images.Select(c => c.Activity))
.Include(m => m.VesselAddresses.Select(c => c.Address))
.Where(m => m.Images.Any(c => c.Activity.Active))
.Where(m => m.Activity.Active)
.Where(m => m.Listed)
.Where(m => m.Activity.User.Active)
.OrderBy(m => Guid.NewGuid())
.Take(4)
.ToListAsync();
If i remove Include(m => m.VesselAddresses.Select(c => c.Address)) or OrderBy from the query, then it works just fine and returns 4 records, but if i leave it as it is, then it returns 8 records, even i specified Take(4)
EDIT
This is almost the same query for apartments table, but this query works just fine and always returns 4 rows:
var apartments = await db.Apartments
.Include(m => m.Images.Select(c => c.Activity))
.Include(m => m.Address)
.Where(m => m.Images.Any(c => c.Activity.Active))
.Where(m => m.Activity.Active)
.Where(m => m.Listed)
.Where(m => m.Activity.User.Active).OrderBy(m => Guid.NewGuid())
.Take(4)
.ToListAsync();
Entity Framework doesn't run the query you are making until you call the ToListAsync, hence my guess would be that you Include can't be translated into SQL so its being ignored by the query builder until after it executes the SQL which because take converts into sql as TOP, means that the include is being applied after Take
moving the .Take(4) after the .ToListAsync() should correct
also i assumed you are using .OrderBy(m => Guid.NewGuid()) to randomise the results i would suggest instead Random.NextDouble() guid is overkill for randomisation

How to include() nested child entity in linq

How do I include a child of a child entitiy?
Ie, Jobs have Quotes which have QuoteItems
var job = db.Jobs
.Where(x => x.JobID == id)
.Include(x => x.Quotes)
.Include(x => x.Quotes.QuoteItems) // This doesn't work
.SingleOrDefault();
Just to be clearer - I'm trying to retrieve a single Job item, and it's associated Quotes (one to many) and for each Quote the associated QuoteItems (One Quote can have many QuoteItems)
The reason I'm asking is because in my Quote Index view I'm trying to show the Total of all the Quote items for each Quote by SUMming the Subtotal, but it's coming out as 0. I'm calling the Subtotal like this:
#item.QuoteItem.Sum(p => p.Subtotal)
I believe the reason I have this issue is that my Linq query above isn't retrieving the associated QuoteItems for each Quote.
To get a job and eager load all its quotes and their quoteitems, you write:
var job = db.Jobs
.Include(x => x.Quotes.Select(q => q.QuoteItems))
.Where(x => x.JobID == id)
.SingleOrDefault();
You might need SelectMany instead of Select if QuoteItems is a collection too.
Note to others; The strongly typed Include() method is an extension method so you need to include using System.Data.Entity; at the top of your file.
The method in the accepted answer doesn't work in .NET Core.
For anyone using .NET Core, while the magic string way does work, the cleaner way to do it would be ThenInclude:
var job = db.Jobs
.Where(x => x.JobID == id)
.Include(x => x.Quotes)
.ThenInclude(x => x.QuoteItems)
.SingleOrDefault();
Source: Work with data in ASP.NET Core Apps | Microsoft Learn
This will do the job (given that we are talking entity framework and you want to fetch child-entities):
var job = db.Jobs
.Include(x => x.Quotes) // include the "Job.Quotes" relation and data
.Include("Quotes.QuoteItems") // include the "Job.Quotes.QuoteItems" relation with data
.Where(x => x.JobID == id) // going on the original Job.JobID
.SingleOrDefault(); // fetches the first hit from db.
For more information about the Include statement have a look at this: https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectquery-1.include
This answer has been getting upvotes throught the years, so I'd just like to clarify, try https://stackoverflow.com/a/24120209/691294 first. This answer is for those cases where all else fails and you have to resort to a black magic solution (i.e. using magic strings).
This did the trick for me as #flindeberg said here .
Just added checking if there are children in each parent item in the list
List<WCF.DAL.Company> companies = dbCtx.Companies.Where(x=>x.CompanyBranches.Count > 0)
.Include(c => c.CompanyBranches)
.Include("CompanyBranches.Address")
.ToList();

Why am I getting a Compile time error in this linq query?

I am getting a compile time error when compiling the below code and I can't see why:
Relations are many to many relations
var contacts = groups_to_querry
.SelectMany(x => x.Contacts)
.Where(x => x.ID == Guid.Empty)
.SelectMany(p => p.ContactDetails)
.Where(x => x.ID == Guid.Empty)
.SelectMany(x => x.Contacts); //This line gives me a compile time error
//Error : The Type argumetns for method 'System.Linq.Enumerable.SelectMany<TSource,Tresult>
//(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,
//System.Collections.Generic.IEnumerable<TResult>>)' cannot be infrred from the usage.
//Try specifying the type arguments explicitly
The second time you call for .SelectMany(x => x.Contacts), you are currently working with a collection of ContactDetails. It is doubtful that you would be able to use SelectMany on it. You would need to use Select instead.
SelectMany is used when you want to select multiple collections of items and put them into one IEnumerable. Select is used on individual fields. Since you are working with objects of type ContactDetail (which I assume can only have one contact), you would need use Select
EDIT: Here is what you're doing in a nutshell, step by step:
groups_to_querry.SelectMany(x => x.Contacts): From all the groups that I want to query select all of their many contacts. Each group has many contacts, so put them all into a single IEnumerable collection of type Contact
.Where(x => x.ID == Guid.Empty): ...but only those Contacts with an empty ID
.SelectMany(p => p.ContactDetails): Then select all of those Contacts' many ContactDetails. Each Contact has many ContactDetails, so put them all into a single IEnumerable collection of type ContactDetail
.Where(x => x.ID == Guid.Empty): ...but only those ContactDetails with an empty ID
.SelectMany(x => x.Contacts);: Now select each of the ContactDetails' many Contacts. However, since the compiler knows that there is a one-to-many relationship between Contacts and ContactDetails (and not the other way around) that statement is not possible, and thus shows a compile error
I'm interpreting your intended query as "from multiple groups of contacts, select all contacts that have ID=Guid.Empty and also have details that all have ID=Guid.Empty".
The way your code is actually interpreted is "from all contacts that have Guid.Empty, select all details that have Guid.Empty, and from those details select all contacts". The first problem is that you end up selecting from details. This means the final SelectMany should be a Select, because x.Contacts here refers to the many-to-one relationship from details to contacts.
The second problem is that the result will contain duplicates of contacts, because the same contact is included for each details. What you should be doing instead is filtering the contacts directly based on their details collections, like this:
groups_to_query
.SelectMany(g => g.Contacts)
.Where(c => c.ID == Guid.Empty)
.Where(c => c.ContactDetails.All(d => d.ID == Guid.Empty))
Note this would also select contacts that have zero details, which is different behavior from your query, so I'm not sure if it's what you want. You could add another filter for ContactDetails.Any() if not.
Edit: Since you're using Entity Framework, the above probably won't work. You may need to select the details in a subquery and then filter in memory after it executes:
var queryResult =
groups_to_query
.SelectMany(g => g.Contacts)
.Where(c => c.ID == Guid.Empty)
.Select(c => new {
contact = c,
detailIDs = c.ContactDetails.Select(d => d.ID)
}).ToList();
var contacts =
queryResult
.Where(r => r.detailIDs.All(id => id == Guid.Empty))
.Select(r => r.contact);

Linq, unable to list only distinct results

I have three tables, car_type, car_manufacturer and car_model. When the user click on the particular vehicle type they want to browse, I'd like to show them a list of available manufacturers. The problem is the list of manufacturers is not distinct or unique. So if my db has three models from Mazda, Mazda will show up on the list 3 times. This is my controller:
public ActionResult Browse(string click_string)
{
var x = carDB.Models
.Include(b => b.Manufacturer)
.Include(a => a.VehicleType)
.Where(a => a.VehicleType.TypeName == click_string);
return View(x.ToList());
}
How can I write this to remove redundant listings? This is all new to me, so go easy on me.
You have to query for Manufacturers, not for Vehicles:
var x = carDB.Models.Where(a => a.VehicleType.TypeName == click_string)
.Select(a => a.Manufacturer)
.Distinct();
It usually works well to try and avoid Distinct altogether. You want manufacturers? Get manufacturers. And determine from there which ones you need: the ones that produce models that have click_string in their type name:
carDB.Manufacturers.Where(manufacturer => manufacturer.Models
.Any(model => model.VehicleType.TypeName == click_string))
You may want to include Models and/or VehicleType, that depends on what you want to show in the view.
First try doing a .Distinct() at the end of the query, if it does not work you might need to provide a custom comparer for the .Distinct()
You should be able to use .Distinct to return the distinct elements.
var x = carDB.Models
.Include(b => b.Manufacturer)
.Include(a => a.VehicleType)
.Where(a => a.VehicleType.TypeName == click_string)
.Distinct();
add distinct
var x = carDB.Models
.Include(b => b.Manufacturer)
.Include(a => a.VehicleType)
.Where(a => a.VehicleType.TypeName == click_string)
.Select(y => y)
.Distinct();
The .Select() might be a bit verbose but without trying it in my visual studio i put it in there for saftey

Categories