Although you forgot to write your class definitions, it seems that you have a HotFix
class. Every HotFix
has a sequence of zero or more AssociatedPRs
and a sequence of zero or more Details
.
Ever Detail
has at least one numeric property Available
.
You want all HotFixes
, each with all its AssociatedPRs
, and all Details
that have a property Available
value equal to 1 (didn't you mean that available is a Boolean?)
When using entity framework, people tend to use include to get an item with its sub-items
. This is not always the most efficient method, as it gets the complete row of a table, inclusive all the properties that you do not plan to use.
For instance, if you have a one-to-many relationship, Schools with their Students
, then each Student
will have a foreign key to the School
that this `Student attends.
So if School [10] has 1000 Students
, then every Student
will have a foreign key to the School
with a value 10. If you use Include
to fetch School [10] with its Students
, then this foreign key value is also selected, and sent a 1000 times. You already know it will equal the Schools
primary key value, hence it is a waste of processing power to transport this value 10 a 1001 times.
When querying data, always use Select, and Select only the properties you actually plan to use. Only use Include if you plan to update the fetched data.
Another good advice is to use plurals to describe sequences and singulars to describe one item in your sequence
Your query will be:
var result = context.HotFixes.Select(hotfix => new
{
// Select only the hotfix properties you actually plan to use:
Id = hotfix.Id,
Date = hotfix.Date,
...
AssociatedPRs = hotfix.AssociatedPRs.Select(accociatedPr => new
{
// again, select only the associatedPr properties that you plan to use
Id = associatedPr.Id,
Name = associatedPr.Name,
...
// foreign key not needed, you already know the value
// HotFixId = associatedPr.HotFixId
})
.ToList(),
Details = hotfix.Details
.Where(detail => detail.Available == 1)
.Select(detail => new
{
Id = detail.Id,
Description = detail.Description,
...
// not needed, you know the value:
// Available = detail.Available,
// not needed, you know the value:
// HotFixId = detail.HotFixId,
})
.ToList(),
});
I used anonymous type. You can only use it within the procedure in which the anonymous type is defined. If you need to return the fetched data, you'll need to put the selected data in a class.
return context.HotFixes.Select(hotfix => new HotFix()
{
Id = hotfix.Id,
Date = hotfix.Date,
...
AssociatedPRs = hotfix.AssociatedPRs.Select(accociatedPr => new AssociatedPr()
{
... // etc
Note: you still don't have to fill all the fields, unless your function requirement specifically states this.
It might be confusing for users of your function to not know which fields will actually be filled and which ones will not. On the other hand: when adding items to your database they are already accustomed not to fill in all fields, for instance the primary and foreign keys.
As a solution for that not all fields are filled, some developers design an extra layer: the repository layer (using the repository pattern). For this they create classes that represent the data that people want to put into storage and want to save into the storage. Usually those people are not interested in that the data is saved in a relational database, with foreign keys and stuff. So the repository classes won't have the foreign keys
The advantage of the repository pattern is, that the repository layer hides the actual structure of your storage system. It even hides that it is a relational database. It might also be in a JSON-file. If the database changes, users of the repository layer don't have to know about this, and probably don't need to change as well.
A repository pattern also makes it easier to mock the database for unit testing: since users don't know that the data is in a relational database, for the unit test you can save the date in a JSON file, or a CSV-file or whatever.
The disadvantage is that you need to write extra classes that holds the data that is to be put into the repository, or fetched from the repository.
Whether it is wise to add this extra layer or not, depends on how often you expect your database to change layout in the future, and how good your unit tests need to be.