我是Linq的新手,希望能对某个特定的查询有一些明确的了解。
我有两个表(为了演示而简化):
表:客户
CustomerId | Name
1 | John Smith
2 | Peter James
表格:订单
id | CustomerId | Total
1 | 1 | $100
2 | 1 | $200
示例客户收件人:
public class CustomerDto
{
public long CustomerId { get; set; }
public string Name{ get; set; }
public CustomerOrder[] CustomerOrderList{ get;set;}
}
Linq example for left outer join,它们返回string.empty如果连接失败,我不确定当我返回一个对象而不是一个字符串时,它们的等价性。
我的Linq查询如下所示。我已经使用DefaultIfEmpty()来帮助左外连接,但是考虑到我正在处理我的对象,我不确定如果什么都没有的话如何返回null。
IQueryable<CustomerDto> search =
from customer in _database.Customer
join customerOrder in _database.CustomerOrder on customer.CustomerId equals customerOrder.CustomerId into CS
from subCustomerSale in CS.DefaultIfEmpty()
select new CustomerDto
{
CustomerId = customer.CustomerId,
Name = customer.Name,
CustomerOrderList = subCustomerSale
};
如前所述,我希望返回一个订单列表,而不是每个订单一行。因此,应该返回两条记录(两个客户),一条包含订单列表,另一条不包含任何订单。
我该如何做到这一点?
2条答案
按热度按时间pbossiut1#
使实体更易于使用的第一步是确保导航属性已设置。如果表名为“Order”,则实体可以是Order,也可以根据需要重命名为CustomerOrder。Customer实体可以有一个
ICollection<Order>
集合,如果使用了一致的命名约定和规范化,EF可以自动Map该集合。(否则可以提供显式Map)例如:
从这里,您将Customers投影到CustomerDTO,因此我们还应该将Orders投影到OrderDTO。请注意,使用导航属性时,我们不必显式连接实体。我们甚至不必通过
Include()
立即加载相关数据。如果/当我们希望使用实体而不是投影时,后者将适用。产生的查询结果如下所示:
其优点是不需要显式地编写连接表达式。EF可以帮助大大简化访问相关数据,而不仅仅是方便使用Linq作为SQL的替代。如果该客户没有订单,这将返回一个空列表而不是#null。如果没有任何订单,可能会替换为#null,但最坏的情况是,它可能会在结果具体化后进行后处理,即:
实际上,它只是归结为消费者是否知道总是存在一个Orders集合,如果没有订单,该集合将为空,或者只有在有订单时,Orders集合中才存在。(通过JSON等序列化)
需要考虑的重要细节:在进行筛选时,如填写搜索条件、请在
Select
之前执行此操作,因为IQueryable
正在处理实体,因此您对表字段具有完全访问权限。在Select
之后添加Where
子句会将可用字段限制为您为DTO选择的字段。Select
中有一个ToList
用于构建Orders集合。但只有当主查询为时才会执行。(例如IQueryable
上等待的async
操作)投影到DTO时,请确保不要将DTO和实体混合,例如:
...这可能很诱人。这里的问题是“c.Orders”将返回Order实体的集合。这些Orders可能引用了您不需要/不想向使用者公开的其他实体或信息。访问引用可能会导致延迟加载成本、空引用或异常(即已处理的DbContext),具体取决于它们发生的时间/位置。
e4yzc0pl2#
只需将三元条件设置为如下所示: