C# e(可选)左侧外部联接的右侧,您需要考虑它可能是null。在LINQ to对象中,需要使用带nullcheck的条件运算符或null合并运算符。在LINQtoEntities中,这是由SQL自然处理的,但您需要将不可为null的字段的结果类型更改为其可为null的等效字段。在匿名投影中,它是通过如上所示的显式转换实现的
最后,为什么使用查询语法?当然,它可以用方法语法编写(C# e(可选)左侧外部联接的右侧,您需要考虑它可能是null。在LINQ to对象中,需要使用带nullcheck的条件运算符或null合并运算符。在LINQtoEntities中,这是由SQL自然处理的,但您需要将不可为null的字段的结果类型更改为其可为null的等效字段。在匿名投影中,它是通过如上所示的显式转换实现的,c#,entity-framework,entity-framework-core,linq-to-entities,ef-core-3.1,C#,Entity Framework,Entity Framework Core,Linq To Entities,Ef Core 3.1,最后,为什么使用查询语法?当然,它可以用方法语法编写(SelectMany,如Steve Py的回答),但由于EF Core团队似乎在测试编译器生成的LINQ构造,如果使用“错误”重载/模式,您很容易遇到EF Core错误。这里的“错”并不是真的错,只是EF核心翻译人员没有考虑到的东西。这里的“正确”是使用带有结果选择器的SelectMany重载: var query=context.Customers .SelectMany(c=>c.Orders.DefaultIfEmpty(),(c,o)
SelectMany
,如Steve Py的回答),但由于EF Core团队似乎在测试编译器生成的LINQ构造,如果使用“错误”重载/模式,您很容易遇到EF Core错误。这里的“错”并不是真的错,只是EF核心翻译人员没有考虑到的东西。这里的“正确”是使用带有结果选择器的SelectMany
重载:
var query=context.Customers
.SelectMany(c=>c.Orders.DefaultIfEmpty(),(c,o)=>new
{
CustomerId=c.Id,
OrderId=(长?)o.Id,
总计=(整数?)o总计
});
使用查询语法就不会有这样的问题。您到底想如何分页?你是说要基于数据行分页,即使这涉及将单个客户拆分为两页?是的,@benm在发布的模型中似乎有一些类型不匹配-
字符串类型客户Id
顺序vs长类型Id
客户,因此CustomerId
不能是FK,除非Id
不是PK,或者关系已配置为使用其他Customer
属性作为备用键。你能澄清一下吗?因为用一个适当的模型来完成外部连接是很简单的——你需要考虑的是,在投影中,一些不可空的字段变成空的。你是说要基于数据行分页,即使这涉及将单个客户拆分为两页?是的,@benm在发布的模型中似乎有一些类型不匹配-字符串类型客户Id
顺序vs长类型Id
客户,因此CustomerId
不能是FK,除非Id
不是PK,或者关系已配置为使用其他Customer
属性作为备用键。你能澄清一下吗?因为用一个适当的模型做左外连接是很简单的——你需要考虑的是,在投影中,一些非可空的字段变成可空的。这与在下面的Steve Py的答案中所提出的第一个解决方案本质上是相同的吗?您应该知道,?。
在表达式树中(仍然)不受支持,因此您只会得到编译时错误。这与下面@Steve Py给出的答案中的第一个解决方案基本相同吗?该解决方案在EF Core中不再有效?如果您曾经对IQueryable
,使用LINQ,您应该知道,?。
在表达式树中(仍然)不受支持,所以您只会得到编译时错误。这个答案确实帮助我理解了LINQ查询的“左连接”实现。非常感谢您不仅写了一个答案,而且还漂亮地解释了它:)这个答案真的帮助我理解了LINQ查询的“左连接”实现。非常感谢您不仅写了一个答案,还漂亮地解释了它:)
public class Order
{
public long Id { get; set; }
public string CustomerId { get; set; }
public int Total {get; set;}
public virtual Customer Customer{ get; set; }
}
public class Customer : ApplicationUser
{
public long Id {get; set;}
public virtual ICollection<Order> Orders { get; set; }
}
Customer Order Total
-------- ----- -----
1 null null
2 100 5
2 101 199
3 null null
4 200 299
4 201 399
var results = context.Customers
.SelectMany(c => c.Orders.DefaultIfEmpty()
.Select(o => new { c.CustomerId, o.OrderId, o.Total })
);
[Serializable]
public class OrderData
{
public int CustomerId { get; set; }
public int? OrderId { get; set; }
public int? Total { get; set; }
}
var results = context.Customers
.Where(c => !c.Orders.Any())
.Select(c => new OrderData { CustomerId = c.CustomerId, OrderId = null, Total = null })
.Union(context.Customers
.SelectMany(c => c.Orders.Select(o => new OrderData
{
CustomerId = c.CustomerId,
OrderId = o.OrderId,
Total = o.Total
})));
from customer in context.Customers
join order in context.Orders on customer equals order.Customer into gj
from suborder in gj.DefaultIfEmpty()
select new { CustomerId = customer.Id, OrderId = suborder?.Id, Total = suborder?.Total };