Entity Framework Fetch Parent of Parent - c#

I have the following data model:
Agenda->AgendaSection->SectionItem
Starting with the Item, I need to fetch both the Section and Agenda.
I've tried the following (and other things):
IList<AgendaSectionItem> myTasks =
db.AgendaSectionItems.Where(i => i.AssigneeId == currentUser.UserId)
.Include(i => i.AgendaSection)
.Include(s => s.AgendaSection.Agenda)
.ToList();
However, the Agenda ends up being null.
Anyone know how to do this?
Thanks,
Philip

Try removing the first include statement. I have had issues in the past with multiple include calls for whatever reason. It should work with just the one call:
IList<AgendaSectionItem> myTasks =
db.AgendaSectionItems.Where(i => i.AssigneeId == currentUser.UserId)
.Include(s => s.AgendaSection.Agenda)
.ToList();

Related

EF Include sub level entities

I have this query:
var mapping = await context.MAPPING_COMPANIES
.Include(x => x.CUSTOMER_INFO)
.SingleOrDefaultAsync(where => where.AMIN_COMPANY_ID == aminCompanyId);
Now the single MAPPING_COMPANY will have a single CUSTOMER_INFO. I also need to include two more entities of CUSTOMER_INFO: MASTER_ADDRESS_TYPE and MASTER_CUSTOMER_STATUS. I need these two also included or flattened with the CUSTOMER_INFO.
How do I do that? I have experimented with more Including statements and combining Selects and even tried out the ThenIncludeBy.EF6 nuget but to no avail.
Have you tried this?
var mapping = await context.MAPPING_COMPANIES
.Include(x => x.CUSTOMER_INFO)
.Include(x => x.CUSTOMER_INFO.MASTER_ADDRESS_TYPE)
.Include(x => x.CUSTOMER_INFO.MASTER_CUSTOMER_STATUS)
.SingleOrDefaultAsync(where => where.AMIN_COMPANY_ID == aminCompanyId);
You'll need to make sure you do not have any Select(), or I think GroupBy(), in there since Include() only works if query shape matches the entity set.

Entity Framework ignores Include on a Grand Child

My models structure looks like this:
UserProgresses:dbset
LessonProgresses:List // The lesson progress a user has in one course
Lesson:Lesson // The general Lesson class.
Materials:List // A list of lesson materials
When I execute this query:
var progresses = context.UserProgresses
.Include(x => x.LessonProgresses.Select(y => y.Lesson.Materials))
.SingleOrDefault(x => x.Id == progressId);
This is the result I get after executing that query:
foreach (var lessonProgress in progress.LessonProgress)
{
lessonProgress.Lesson // Works
lessonProgress.Lesson.Materials // NULL
}
The interesting thing here is that when I insert the row below inside the loop and on the first line the Materials list gets populated.
context.Lessons.Include(x => x.Material)
.SingleOrDefault(x => x.Id == lesson.Lesson.Id);
I also checked the tables and the data is OK. I suspect that something is wrong with the Include statement.
Thanks.
Try to use a string to specify the relationships
var progresses = context.UserProgresses
.Include("LessonProgresses.Lesson.Materials")
.SingleOrDefault(x => x.Id == progressId);
MSDN documentation
Or try this
var progresses = context.UserProgresses
.Include(u => u.LessonProgress.Select(l => l.Lesson).Select(m => m.Materials))
.SingleOrDefault(x => x.Id == progressId);
So the problem was that the LessonProgress was untracked and was added way back in the callstack. The Lesson was set using:
lessonProgress.Lesson = context.Lessons.SingleOrDefault(x => x.Id == lessonId);
Without including Materials. After doing a Include there, everything was all good.

OrderByDescending with a date from another table(s)

Hopefully this question is not to confusing. Basically I'm looking for pointers on how to OrderByDecending with a date from relational tables. I have constructed a basic method that looks like it could possibly work but I'm getting errors:
DbSortClause expressions must have a type that is order comparable.
Parameter name: key
I understand what this is saying but I'm not entirely sure how to fix using Linq method syntax.
public BusinessEntities.Application GetLastUpdatedAppliction(int userID)
{
return context.tbl_User_To_Application
.Where(x => x.UserID == userID)
.OrderByDescending
(o => o.tbl_Application.tbl_ApplicationChanges
.Where(oo => oo.ApplicationID == o.ApplicationID)
.Select(s => s.ChangeDate))
.ThenByDescending(t => t.DateAdded)
.Select(y => new BusinessEntities.Application
{
ApplicationID = y.tbl_Application.ApplicationID,
ApplicationName = y.tbl_Application.ApplicationName
}).FirstOrDefault();
}
Basically I have a cross reference table that binds a user to a specific application(Website) Then inside I need to nest into two tables to get the latest changes to the Application with a "ChangesDate". So ideally this would return the last updated application. Then obviously populates my DTO.
I'm still trying to get to grips with Linq method syntax so any help would be greatly appreciated!
Regards,
Tez Wingfield
If you want to order by the last application change date:
(...)
.OrderByDescending(o =>
o.tbl_Application.tbl_ApplicationChanges
.Where(ac => ac.ApplicationID == o.ApplicationID)
.OrderByDescending(ac => ac.ChangeDate)
.First()
.Select(ac => ac.ChangeDate)
)
(...)

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();

Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.IEnumerable<xxx>>' to 'System.Collections.Generic.List<xxx>

I am getting the following error. I googled it for more than a day but I cant find the exact solution, Please help me Thank you
ERROR: Cannot implicitly convert type
System.Collections.Generic.List<System.Collections.Generic.IEnumerable<ADCO.eJMC.EntityDataModel.ShareholderUser>>
to
System.Collections.Generic.List<ADCO.eJMC.EntityDataModel.ShareholderUser>
I used the following code
List<ShareholderUser> list = new List<ShareholderUser>();
list = dataContext.EJMCShareholderApprovals
.Include(s => s.Shareholder.ShareholderUsers)
.Where(e => e.EJMCRequestId == requestId)
.Select(s => s.Shareholder.ShareholderUsers
.Where(x => x.AccessMode == true))
.ToList();
The problem is that at the moment, you're selecting a sequence of sequences - one sequence of ShareholderUser items for each Shareholder. If you just want a list of ShareholderUser items, you need to flatten the results. That's most easily done using SelectMany, which can actually replace your Select call in this case.
List<ShareholderUser> list = dataContext.EJMCShareholderApprovals
.Where(e => e.EJMCRequestId == requestId)
.SelectMany(s => s.Shareholder.ShareholderUsers)
.Where(x => x.AccessMode == true)
.ToList();
Note how breaking the query over multiple lines makes it much simpler to read, too. Also, there's no point in initializing the list variable to a new List<ShareholderUser> if you're then immediately going to give it a different value. I've also removed the Include call, as that was unnecessary - you're explicitly selecting Shareholder.ShareholderUsers in the query, so you don't need to include it.
This should do it?
var list = dataContext.EJMCShareholderApprovals
.Include(s => s.Shareholder.ShareholderUsers)
.Where(e => e.EJMCRequestId == requestId)
.Select(s => s.Shareholder.ShareholderUsers
.Where(x => x.AccessMode == true)).ToList();
though you are doing select on to ShareHolderUsers? Are you trying to get a list of ShareHolderUsers or a list of lists of ShareHolderUsers?
.Select(s => s.Shareholder.ShareholderUsers

Categories