score:2
Separate your concerns. Entities represent domain models. DTOs represent view or consumer models. While EF deals extensively with IQueryable
, your consumers don't typically need IQueryable
, most of the time they just need to be able to enumerate over the results, so IEnumerable
is sufficient. Even within Entities, I don't represent relationships between entities with IQueryable
, instead I use ICollection
.
IQueryable
is a structure to indicate that Linq expressions should be passed through to the source of the data. For EF Linq operations that allows Linq expressions to translated down to SQL. When it comes to dealing with consuming data that has been returned, you can use Linq against IEnumerable
and the like still if you need to further filter or translate results. Entities should never really be passed outside of the scope of their DbContext as features like lazy loading requires the DbContext to be alive, and you cannot reliably assume lazy loads would never be tripped by a consuming view or serializer etc. Entities are data state, view state should be kept separate.
Given a domain model of entities consisting of Boards, BoardLists, and Cards my entities would look something like:
public class Board
{
public int BoardId { get; set; }
public string Name { get; set; }
public virtual ICollection<BoardList> Lists { get; set; } = new List<BoardList>();
}
public class BoardList
{
public int BoardListId { get; set; }
public string Name { get; set; }
public Virtual ICollection<Card> Cards { get; set; } = new List<Card>();
}
public class Card
{
public long CardId { get; set; }
public CardType Type { get; set; }
}
The entities would typically reflect all of the data in the underlying table, and the relationships between entities. The child collections are declared as virtual
to accommodate lazy loading if needed, and initialized as a new collection. This accommodates cases where we want to create a new entity and have it's child collection ready to go.
The DTOs represent just the data from the model that the consumer needs. This is where we also consider filtered views of data to suit the view. Rather than ICollection
, we can just use IEnumerable
. You can use ICollection
or IList
but IEnumerable
is generally the recommendation because it says to the consumer "here's a set of data you can read, but don't try adding to it, etc.".
public class BoardDTO
{
public int BoardId { get; set; }
public string Name { get; set; }
public IEnumerable<BoardListDTO> Lists { get; set; }
}
public class BoardListDTO
{
public int BoardListId { get; set; }
public string Name { get; set; }
public IEnumerabe<CardDTO> Cards { get; set; }
}
public class CardDTO
{
public long CardId { get; set; }
public CardType Type { get; set; }
}
In this example the DTOs look identical to the entities. Typically though, the entity and underlying tables will contain many fields, where our view models or DTOs only need a few fields, or relationships.
When it comes to populating your collection of DTOs for the view, you leverage projection using Select
. So if you want to list all boards that have at least 1 card of cardType 1, and then return those boards with only their CardType 1 cards:
This is assuming that CardType is mapped as an Enum. If CardType is mapped to a table, then it will need an entity / DTO as well.
var boards = context.Boards
.Where(b => b.Lists
.Any(bl => bl.Cards.Any(c => c.CardType == CardTypes.Type1)))
.Select(b => new BoardDTO
{
BoardId = b.BoardId,
Name = b.Name,
Lists = b.Lists.Select(bl => new BoardListDTO
{
BoardListId = bl.BoardListId,
Name = bl.Name,
Cards = bl.Cards.Where(c => c.CardType == CardTypes.Type1)
.Select(c => new CardDTO
{
CardId = c.CardId,
CardType = c.CardType
}).ToList()
}).ToList()
}).ToList()
This example does a lot of 1-to-1 mapping between entity and DTO. Often DTOs are flattened to represent a structure that the consuming view can use. This might consolidate the consumer model to just the Board and a list of cards with the board list name appearing within the related card DTO. (So a board DTO would just display a list of Cards that include their BoardList name.)
DTO/ViewModels like this are useful for filtering data because entities should always reflect the true data state so you cannot return entities with just a subset of some of the related cards. The Board will always associate with all board lists associated to it, and all cards associated to those. Using DTOs and Select
you can customize what data the view/consumer actually needs.
Source: stackoverflow.com
Related Articles
- C# IQueryable - How to query a list inside another list
- Using Linq query inside List<T>.AddRange to conditionally add elements from one list to another
- LINQ Filter List inside another LINQ Query
- C# LINQ Find List Inside Another List, Better way to code this than a foreach loop
- How to make the Linq Query as efficient when select list inside the another select list
- How to set order of IQueryable query based on another list using linq in c#?
- Linq query to return items of list with another list inside it
- How to use one Column of Sql query into another sql query inside C# Code
- LINQ query to find if items in a list are contained in another list
- Using LINQ, select list of objects inside another list of objects
- Linq query to filter id inside a list of list c#
- Execute expression on another IQueryable source
- Linq query - List within another list
- linq - how do you do a query for items in one query source that are not in another one?
- How to use LINQ to query list of strings that do not contain substring entries from another list
- LINQ query for finding one item in list AND verifying list does not contain another item
- C# linq Select objects in list looking inside another list of objects
- List Inside IQueryable Object
- LINQ query returns old results when source list is re-initialized
- Copy a list of objects to another typed list in one line of code
- C# LINQ - Return a typed filtered list based on a child property that is inside another list
- Linq to query results of list of objects based on another list
- Using the Iqueryable list result from one query as the parameter for another?
- c# find item in list returned by LINQ query and compare its value with another item in list
- Linq query for my List<Square> to see if an item of one list is in another
- Assign IEnumerable list inside linq query
- LINQ query to return list where condition is based on another table
- Complex Linq query to select list items with another list property containing a value
- Create a entity framework LINQ group join query to return a subset of properties from DTO and a list of another DTO, ASP.net core
- Query all the entities where list property contains everything in another list
- This keyword in C#, Linq?
- LINQ to SQL - Tracking New / Dirty Objects
- I have some quite simple SQL that I can trying to change to either Linq or LLBLGEN
- using LINQ where(lamdaexp).First and First(lambdaexp)
- C# Linq over XML => Lambda Expression
- Sum a IQueryable Column with where clause
- Linq GroupBy multiple fields
- LINQ: conditional joins based on preselected data
- Update an item in the list not working
- C# - Group Objects by Property Value
- Unable to read XML correctly
- Applying LINQ filters based on a multi-dimensional array
- "The null value cannot be assigned to a member with type System.Int32 which is a non-nullable value type."
- Get Monthly-wise data using EntityFramework
- C# How do i merge two DataTable Row into one row
- How to update a global variable inside `where` clause in LINQ?
- Call Enumerable Average via expression
- Linq query returns values instead of setting values (With example) in C#
- Linq expressions as params
- Including related entities across a many-to-many relationship