这两种方法在使用LINQ执行Left Outer Join方面有何不同,因为我使用了两个买方和供应商列表,并通过公共区域加入它们以查找位于同一区域的供应商和买方。
class Supplier
{
public string Name { get; set; }
public string District { get; set; }
}
class Buyer
{
public string Name { get; set; }
public string District { get; set; }
}
List<Buyer> buyers = new List<Buyer>()
{
new Buyer() { Name = "Johny", District = "Fantasy District" },
new Buyer() { Name = "Peter", District = "Scientists District" },
new Buyer() { Name = "Paul", District = "Fantasy District" },
new Buyer() { Name = "Maria", District = "Scientists District" },
new Buyer() { Name = "Joshua", District = "EarthIsFlat District" },
new Buyer() { Name = "Sylvia", District = "Developers District" },
new Buyer() { Name = "Rebecca", District = "Scientists District" },
new Buyer() { Name = "Jaime", District = "Developers District" },
new Buyer() { Name = "Pierce", District = "Fantasy District" }
};
List<Supplier> suppliers = new List<Supplier>()
{
new Supplier() { Name = "Harrison", District = "Fantasy District" },
new Supplier() { Name = "Charles", District = "Developers District" },
new Supplier() { Name = "Hailee", District = "Scientists District" },
new Supplier() { Name = "Taylor", District = "EarthIsFlat District" }
};
首先:
var suppliersAndBuyers = from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District into buyersGroup
select buyersGroup.DefaultIfEmpty(
new Buyer()
{
Name = string.Empty,
District = s.District
});
foreach (var item in suppliersAndBuyers)
{
foreach (var buyer in item)
{
Console.WriteLine($"{buyer.District} {buyer.Name}");
}
}
第二种方法:
var suppliersAndBuyers = from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District into buyersGroup
from bG in buyersGroup.DefaultIfEmpty()
select new
{
Name = bG.Name == null ? string.Empty : bG.Name,
s.District,
};
foreach (var item in suppliersAndBuyers)
{
Console.WriteLine($"{item.District} {item.Name}");
}
两者都产生完全相同的输出,这是我们输出结果的唯一方法吗?我应该使用哪一个?
编辑:第一种方法返回IEnumerable<IEnumerable<Buyer>>
,第二种方法返回IEnumerable<AnonymousType>
,这是两者之间唯一有意义的区别,它们返回的是什么类型,这是唯一的两种方法之间的决定因素,我想要一种类型或匿名类型?
答案 0 :(得分:3)
好的。从我看到的内容:(A)
var suppliersAndBuyers = from s in suppliers
orderby s.District
列举供应商列表。这很明显。现在,将其加入买家名单:
var suppliersAndBuyers = from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District
这会创建匹配(一些我不知道类型的对象,因为我前面没有正常的Visual Studio实例)。但是,例如,它就像Harrison:Jonnie, Hailee:Peter, ...
。现在我们可以根据这些匹配(由变量b
和s
表示)创建一个IEnumerable对象:
var suppliersAndBuyers = from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District
select new {
Supplier = s, Buyer = b
}
这将创建一个IEnumerable的匿名类型,每个对象代表一对供应商和买方。
var suppliersAndBuyers = from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District into buyersGroup
但是你决定做的是左连接,如你的标题所示。它的作用是,它会在列表(A)中创建列表中的每个元素,并从买方列表中匹配所有匹配对象的IEnumerable
。这产生了一系列匹配。例如,对于 Harrison ,suppliers
列表中的第一个条目,您将获得包含 Johnny 的<{1}}, Paul < / em>和皮尔斯。与IEnumerable
列表中的其他元素相同,按其suppliers
排序。
这就是你以District
结束的原因。因为对于每个供应商(第一个IEnumerable维度),您有一个IEnumerable<IEnumerable<Buyer>>
的“列表”(第二维+类型解释)
在我看来,合并空条目已经过时了,因为你不应该有Buyer
s,而只是空null
s,然后在迭代它们时,你就不会碰到任何元素。 (虽然我不确定最后一段,因为我从来没有编译代码所以我不知道)
现在,对于合并部分,第一个示例为每个条目创建一个新的IEnumerable
对象,然后取Buyer
。对于第二个示例,它首先创建第一个维度和第二个维度FULL IEnumerables,然后再次迭代时,它合并空值。正如我在评论中提到的那样,这是一个不必要的循环。
答案 1 :(得分:2)
在第一个中,由于IEnumerable
,您返回Buyer
new Buyer()
,而在第二个中,您返回了IEnumerable
select new
3}}因为%let usager_entr =12121212,34343434,56565656;
%put &usager_entr;
%let New_usager_entr=%sysfunc(prxchange(s/([1-9]+)/"$1"/, -1,%quote(&usager_entr)));
%put &New_usager_entr;
。他们有利有弊,优点和缺点。
您可以查看以下答案来决定:
答案 2 :(得分:2)
好的,我花了一段时间来解读这个:)
所以事情就是你正在做的第一个例子
选择buyersGroup.DefaultIfEmpty( 新买家() { Name = string.Empty, 区= s.District });
该行的含义是...选择所有买家小组以及它是否为空回报new Buyer() { Name = string.Empty, District = s.District }
(如您将其定义为默认值)
在第二个例子中
来自买家小组中的bG.DefaultIfEmpty() 选择新的 { Name = bG.Name == null? string.Empty:bG.Name, s.District, };
如果group为emptypilot.DefaultIfEmpty()并且仅在执行select之后,您首先定义默认值。密切关注DefaultIfEmpty括号内容。
修改
我似乎无法找到一个完全使用DefaultIfEmpty的理由......不要认为你可以在左侧获得null而需要它...而且它可能会使代码稍微容易一些了解。
答案 3 :(得分:2)
见Enumerable.DefaultIfEmpty。在您的第一个示例中,如果在联接中找不到new Buyer
实例(即您的外部联接),则将Buyer
传递给该扩展方法作为在联接中返回的默认值。之后没有选择语句,因此结果的结果将作为IEnumerable<IEnumerable<Buyer>>
在结果中建模。
您的第二个查询确实使用带有匿名投影的select
,这就是为什么会导致展平的集合。
纯粹看你的结果类型
您可以通过删除第二个查询中的select
来模仿第一个查询的结果
from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District into buyersGroup
from bG in buyersGroup.DefaultIfEmpty();
要转向另一个方向,您可以在第一个查询的末尾添加相同的select语句。
from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District into buyersGroup
select buyersGroup.DefaultIfEmpty(
new Buyer()
{
Name = string.Empty,
District = s.District
))
select new
{
Name = bG.Name == null ? string.Empty : bG.Name,
s.District,
};
至于为何使用一个而不是另一个,这是一个意见问题,也取决于具体情况。在非常简单的示例中,没有使用投影的任何其他上下文将是更可读的答案。如果你想将结果传递给另一个方法或者从它正在执行的方法返回,那么IEnumerable<IEnumerable<Buyer>>
将是唯一的方法(如果约束到这两个样本)。如果您必须使用数据存储执行此操作,那么我建议您对查询进行概要分析,以查看正在执行的查询和配置文件。
简而言之,没有正确/错误的答案,直到你有一个特定的现实世界的情况,这两者之间存在可衡量的差异,而且该措施的重量取决于那种情况。