c# - C# IQueryable - 如何在另一个列表中查询一个列表
问题描述
我正在一个项目中工作,该项目显示一个包含 ListDTO 的 IQueryable 的 BoardDto。每个 ListDTO 都包含一个 CardDTO 的 IQueryable。
我怎样才能让我的 BoardDto 对象加载了所有 ListDtos ,但只有他们的 Cards where CardType = 1
?
public class BoardDTO
{
public int BoardId { get; set; }
public string Nome { get; set; }
public IQueryable<BoardListDTO> Lists { get; set; }
}
public class BoardListDTO
{
public int BoardListId { get; set; }
public string Name { get; set; }
public IQueryable<CardDTO> Cards { get; set; }
}
public class CardDTO
{
public long CardId { get; set; }
public CardType Type { get; set; }
}
编辑
下图说明了项目必须如何构成 DTO。
解决方案
这解决了我的问题:
var query = _context.Boards
.AsNoTracking()
.ProjectTo<BoardDto>(_mapper)
.Where(x => x.BoardId == id)
.Select(x => new BoardDto
{
BoardId = x.BoardId,
Name = x.Name,
Lists = x.Lists.Select(list => new BoardListSearchDTO
{
Cards = list.Cards.Where(x => x.Type == 1),
}).FilterLists(filterListsBy, listSearchValue)
});
解决方案
分开你的顾虑。实体代表领域模型。DTO 代表视图或消费者模型。虽然 EF 广泛处理IQueryable
,但您的消费者通常不需要IQueryable
,大多数时候他们只需要能够枚举结果,这就IEnumerable
足够了。即使在实体中,我也不用 表示实体之间的关系IQueryable
,而是使用ICollection
.
IQueryable
是一种结构,用于指示应将 Linq 表达式传递到数据源。对于允许 Linq 表达式向下转换为 SQL 的 EF Linq 操作。在处理已返回的消费数据时,IEnumerable
如果您需要进一步过滤或翻译结果,您仍然可以使用 Linq against 等。实体永远不应真正传递到其 DbContext 范围之外,因为延迟加载等功能需要 DbContext 处于活动状态,并且您不能可靠地假设延迟加载永远不会被消费视图或序列化程序等触发。实体是数据状态,视图状态应该分开。
给定一个由 Boards、BoardLists 和 Cards 组成的实体的域模型,我的实体看起来像:
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; }
}
实体通常会反映基础表中的所有数据以及实体之间的关系。如果需要,子集合被声明为virtual
适应延迟加载,并初始化为新集合。这适用于我们想要创建一个新实体并准备好它的子集合的情况。
DTO 仅代表消费者需要的模型中的数据。这也是我们考虑过滤数据视图以适应视图的地方。而不是ICollection
,我们可以只使用IEnumerable
. 您可以使用ICollection
orIList
但IEnumerable
通常是建议,因为它对消费者说“这是一组您可以阅读的数据,但不要尝试添加等等”。
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; }
}
在此示例中,DTO 看起来与实体相同。但通常情况下,实体和基础表将包含许多字段,而我们的视图模型或 DTO 只需要几个字段或关系。
在为视图填充 DTO 集合时,您可以使用Select
. 因此,如果您想列出所有至少有 1 张 cardType 1 卡的棋盘,然后返回那些只有 CardType 1 卡的棋盘:
这是假设 CardType 被映射为枚举。如果 CardType 映射到一个表,那么它也需要一个实体/DTO。
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()
这个例子在实体和 DTO 之间做了很多一对一的映射。通常 DTO 被展平以表示消费视图可以使用的结构。这可能会将消费者模型合并为仅董事会和卡列表,其中董事会列表名称出现在相关卡 DTO 中。(因此板 DTO 将只显示包含其 BoardList 名称的卡片列表。)
像这样的 DTO/ViewModels 对于过滤数据很有用,因为实体应该始终反映真实的数据状态,因此您不能只返回一些相关卡片的子集的实体。董事会将始终与与其关联的所有董事会列表以及与其关联的所有卡片相关联。使用 DTO,Select
您可以自定义视图/消费者实际需要的数据。
推荐阅读
- regex - 如何为 VBA 调整正则表达式模式
- css - 需要所有 SVG 属性值的 JSON 格式表
- python - 创建一个新的 pandas 列,它是列表的列表
- regex - 在加拿大邮政编码的第 3 和第 4 个字符之间添加一个空格
- java - Spring Boot - 休眠,回滚成功保存异常
- sql - SQL Query 连接到第二个表中看似循环不存在的匹配项
- vba - VBA:将参数传递给用户表单的问题
- javascript - 以编程方式打开和关闭 React Material UI Snackbar 组件
- html - 使用 :not 伪类定位元素
- laravel - Laravel Mix 构建过程可扩展到许多(50 多个)主题