Entity Framework ignores Include on a Grand Child - c#

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.

Related

Entity Framework Fetch Parent of Parent

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

Check if last Row Entity Framework through Select

Ok people, before bashing, this is a little bit different from what I've investigated.
I currently have the following code:
public async Task<ParticipantTournament> GetParticipantTournamentByDescending(int tournamentId, int participantId)
{
var response = await _dbRepositories.TournamentParticipantMatchRepository
.Where(x => x.TournamentId == tournamentId)
.OrderByDescending(y => y.TournamentMatch.RoundNumber)
.ThenByDescending(y => y.TournamentMatch.Id)
.Include(x => x.Tournament)
.Include(x => x.Participant1)
.Include(x => x.Participant2)
.Include(x => x.TournamentMatch)
.Select(z => new TournamentParticipantMatchLogicDto
{
IsLastMatch = OneTrue() // <==== Problem here,
TournamentParticipantMatch = z
}).Where(x => (x.TournamentParticipantMatch.Participant1Id == participantId || x.TournamentParticipantMatch.Participant2Id == participantId))
.ToListAsync();
return new ParticipantTournament
{
ParticipantMatches = response,
};
}
/**
* This may be something super dumb. But I couldnt' come up with something better.
* How do I detect the latest match?
* http://stackoverflow.com/questions/14687136/check-if-record-is-last-or-first-in-list-with-linq
*
* Well, after digging a little bit, I've found that this is not possible :o
* */
private bool OneTrue()
{
if (!IsLast) return false;
IsLast = false;
return true;
}
I am building a tournament platform. I need to know which is the last match so I can give the players 5 rounds instead of 3. Instead of creating a new column and filling it with false or true, I decided to filter it out. I thought that I could take advantage of LINQ's deferred execution:
I would Select the whole data set from the tournament.
I would then order it by descending and select the first row as the last one. (All the matches were inserted in order, so the biggest id is the last one)
Then filter out which are from the users and which are not.
Possible solutions I think it could work:
- Create a boolean column that will hold "true" value for the last matches.
- Using Specification Pattern (Which I don't know how to apply in this situation. Tried using Expressions and Func but couldn't map them correctly)
- Load all the Ids and select the last one of those Ids. Compare those Ids with the ones that all the users have. Unfortunately this would add an extra roundtrip to the database.
What should I do?
Thanks!
P.S: The OneTrue() method it does what it does, it returns true once, and then it returns false. (Didn't find anything else after a quick Google search)
Edit
For clarification purposes:
The tables show a simulation of the data I currently have. I only need to extract what the current user needs, so I don't need the other 2 rows (which you can see in table #2). Once I select those two rows I exclude the other ones, which could potentially have the last match, but by only selecting this 2 rows I will not know. I'm trying to save any redundancy by trying to query it from the first try. I know that the last match Id is the last of the tournament
So what I was trying to do, is to order them all by descending (because they are in order), and select the last one as the last match.
Could you not just query your subset of data after your return it with linq, like:
var temp = from e in _dbRepositories.TournamentParticipantMatchRepository
where (from f in _dbRepositories.TournamentParticipantMatchRepository
where f.TournamentId == tournamentId)
.Include(x => x.Tournament)
.Include(x => x.Participant1)
.Include(x => x.Participant2)
.Include(x => x.TournamentMatch)
.Select(z => new TournamentParticipantMatchLogicDto
{
IsLastMatch = false, // <==== Problem here,
TournamentParticipantMatch = z
}).Where(x => (x.TournamentParticipantMatch.Participant1Id == participantId || x.TournamentParticipantMatch.Participant2Id == participantId))
.ToListAsync();
int maxResult= temp.Max(t => t.TournamentParticipantMatch.Id);
var update= temp.SingleOrDefault(x => x.TournamentParticipantMatch.Id== maxResult);
if(update!= null)
update.IsLastMatch= true;
This is what I ended up doing. Please if you see any improvements let me know!
public async Task<ParticipantTournament> GetParticipantTournamentByDescending(int tournamentId, int participantId)
{
var lastMatchId = await _dbRepositories.TournamentParticipantMatchRepository
.Where(x => x.TournamentId == tournamentId)
.OrderByDescending(y => y.TournamentMatch.RoundNumber)
.ThenByDescending(y => y.TournamentMatch.Id)
.Select(x => x.Id).FirstOrDefaultAsync();
var response = await _dbRepositories.TournamentParticipantMatchRepository
.Where(x => x.TournamentId == tournamentId)
.Where(x => (x.Participant1Id == participantId || x.Participant2Id == participantId))
.Include(x => x.Tournament)
.Include(x => x.Participant1)
.Include(x => x.Participant2)
.Include(x => x.TournamentMatch)
.ToListAsync();
var logic = response.Select(z=> new TournamentParticipantMatchLogicDto
{
IsLastMatch = z.Id == lastMatchId,
TournamentParticipantMatch = z
})
;
return new ParticipantTournament
{
ParticipantMatches = logic,
};
}

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

LINQ Grouping by ParentId

I have a seemingly simple task that I am having far more trouble than I care to admit doing. I have a hierarchical table that I need to query and display the results grouped by the parent with associated children.
My current LINQ query:
var quests = Questions.Include(q => q.Question2)
.Include(q => q.Sections)
.Include(q => q.QuestionType)
.Include(q => q.AnswerOptions)
.Where(sq => sq.Sections.Any(s => s.SectionId == sectionId))
.OrderBy(q=> q.QuestionId).ThenBy(q => q.ParentQuestionId);
This produces a result set of:
What I want to produce is:
My Question is simply, how can I get the desired results using Lambda syntax.
Update based on Servys' comment.
First line is to make sure all related questions are grouped together.
Second line is to make sure parent question is first.
Third line is to order properly
.OrderBy(q => q.ParentQuestionId == null ? q.QuestionId : q.ParentQuestionId)
.ThenBy(q => q.ParentQuestionId == null ? 0 : 1)
.ThenBy(q => q.DisplayOrder);
So it seems what you're really trying to create here is a tree based structure in which, at the top level, you have all questions with no parent, and then as "child" nodes all questions that have that as a parent.
var questions = GetAllQuestions();//here is where you can put your includes, etc.
var query = questions.Where(q => q.ParentQuestionId != null)
.GroupBy(q => q.ParentQuestionId)
.Select(group => new
{
Parent = questions.First(q => q.QuestionId == group.Key),
Children = group.OrderBy(q => q.DisplayOrder),
})
.OrderBy(group => group.Parent.DisplayOrder);
.OrderBy(x => (x.ParentQuestionId==null?x.QuestionId:x.ParentQuestionId.Value));

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