[英]LEFT OUTER JOIN in LINQ
How to perform left outer join in C# LINQ to objects without using join-on-equals-into
clauses?如何在不使用
join-on-equals-into
子句的情况下对对象执行 C# LINQ 中的左外连接? Is there any way to do that with where
clause?有没有办法用
where
子句做到这一点? Correct problem: For inner join is easy and I have a solution like this正确的问题:对于内部连接很容易,我有这样的解决方案
List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key
select new JoinPair { LeftId = l.Id, RightId = r.Id})
but for left outer join I need a solution.但对于左外连接,我需要一个解决方案。 Mine is something like this but it's not working
我的是这样的,但它不工作
List< JoinPair> leftFinal = (from l in lefts from r in rights
select new JoinPair {
LeftId = l.Id,
RightId = ((l.Key==r.Key) ? r.Id : 0
})
where JoinPair
is a class:其中
JoinPair
是 class:
public class JoinPair { long leftId; long rightId; }
As stated in "Perform left outer joins" :如“执行左外连接”中所述:
var q =
from c in categories
join p in products on c.Category equals p.Category into ps
from p in ps.DefaultIfEmpty()
select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };
If a database driven LINQ provider is used, a significantly more readable left outer join can be written as such:如果使用数据库驱动的 LINQ 提供程序,则可以这样编写可读性更高的左外连接:
from maintable in Repo.T_Whatever
from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()
If you omit the DefaultIfEmpty()
you will have an inner join.如果您省略
DefaultIfEmpty()
您将有一个内部连接。
Take the accepted answer:接受公认的答案:
from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()
This syntax is very confusing, and it's not clear how it works when you want to left join MULTIPLE tables.这种语法非常令人困惑,当您想要左连接 MULTIPLE 表时,不清楚它是如何工作的。
Note笔记
It should be noted that from alias in Repo.whatever.Where(condition).DefaultIfEmpty()
is the same as an outer-apply/left-join-lateral, which any (decent) database-optimizer is perfectly capable of translating into a left join, as long as you don't introduce per-row-values (aka an actual outer apply).应该注意的是
from alias in Repo.whatever.Where(condition).DefaultIfEmpty()
与外部应用/左连接横向相同,任何(体面的)数据库优化器都完全能够将其转换为左连接,只要您不引入每行值(也就是实际的外部应用)。 Don't do this in Linq-2-Objects (because there's no DB-optimizer when you use Linq-to-Objects).不要在 Linq-2-Objects 中执行此操作(因为使用 Linq-to-Objects 时没有 DB-optimizer)。
Detailed Example详细示例
var query2 = (
from users in Repo.T_User
from mappings in Repo.T_User_Group
.Where(mapping => mapping.USRGRP_USR == users.USR_ID)
.DefaultIfEmpty() // <== makes join left join
from groups in Repo.T_Group
.Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
.DefaultIfEmpty() // <== makes join left join
// where users.USR_Name.Contains(keyword)
// || mappings.USRGRP_USR.Equals(666)
// || mappings.USRGRP_USR == 666
// || groups.Name.Contains(keyword)
select new
{
UserId = users.USR_ID
,UserName = users.USR_User
,UserGroupId = groups.ID
,GroupName = groups.Name
}
);
var xy = (query2).ToList();
When used with LINQ 2 SQL it will translate nicely to the following very legible SQL query:当与 LINQ 2 SQL 一起使用时,它将很好地转换为以下非常清晰的 SQL 查询:
SELECT
users.USR_ID AS UserId
,users.USR_User AS UserName
,groups.ID AS UserGroupId
,groups.Name AS GroupName
FROM T_User AS users
LEFT JOIN T_User_Group AS mappings
ON mappings.USRGRP_USR = users.USR_ID
LEFT JOIN T_Group AS groups
ON groups.GRP_ID == mappings.USRGRP_GRP
Edit:编辑:
See also " Convert SQL Server query to Linq query " for a more complex example.另请参阅“ 将 SQL Server 查询转换为 Linq 查询”以获得更复杂的示例。
Also, If you're doing it in Linq-2-Objects (instead of Linq-2-SQL), you should do it the old-fashioned way (because LINQ to SQL translates this correctly to join operations, but over objects this method forces a full scan, and doesn't take advantage of index searches, whyever...):此外,如果您在 Linq-2-Objects(而不是 Linq-2-SQL)中执行此操作,则应该以老式方式执行此操作(因为 LINQ to SQL 将其正确转换为连接操作,但在对象上使用此方法强制进行全面扫描,并且不利用索引搜索,但是...):
var query2 = (
from users in Repo.T_Benutzer
join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
from mappings in tmpMapp.DefaultIfEmpty()
from groups in tmpGroups.DefaultIfEmpty()
select new
{
UserId = users.BE_ID
,UserName = users.BE_User
,UserGroupId = mappings.BEBG_BG
,GroupName = groups.Name
}
);
Using lambda expression使用 lambda 表达式
db.Categories
.GroupJoin(db.Products,
Category => Category.CategoryId,
Product => Product.CategoryId,
(x, y) => new { Category = x, Products = y })
.SelectMany(
xy => xy.Products.DefaultIfEmpty(),
(x, y) => new { Category = x.Category, Product = y })
.Select(s => new
{
CategoryName = s.Category.Name,
ProductName = s.Product.Name
});
Now as an extension method:现在作为扩展方法:
public static class LinqExt
{
public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey,
Func<TLeft, TRight, TResult> result)
{
return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
.SelectMany(
o => o.r.DefaultIfEmpty(),
(l, r) => new { lft= l.l, rght = r })
.Select(o => result.Invoke(o.lft, o.rght));
}
}
Use like you would normally use join:像通常使用 join 一样使用:
var contents = list.LeftOuterJoin(list2,
l => l.country,
r => r.name,
(l, r) => new { count = l.Count(), l.country, l.reason, r.people })
Hope this saves you some time.希望这可以节省您一些时间。
An implementation of left outer join by extension methods could look like通过扩展方法实现左外连接可能看起来像
public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
, Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
{
if (outer == null)
throw new ArgumentException("outer");
if (inner == null)
throw new ArgumentException("inner");
if (outerKeySelector == null)
throw new ArgumentException("outerKeySelector");
if (innerKeySelector == null)
throw new ArgumentException("innerKeySelector");
if (resultSelector == null)
throw new ArgumentException("resultSelector");
return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
}
static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
IEnumerable<TOuter> outer, IEnumerable<TInner> inner
, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
, Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
{
var innerLookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var outerElment in outer)
{
var outerKey = outerKeySelector(outerElment);
var innerElements = innerLookup[outerKey];
if (innerElements.Any())
foreach (var innerElement in innerElements)
yield return resultSelector(outerElment, innerElement);
else
yield return resultSelector(outerElment, default(TInner));
}
}
The resultselector then has to take care of the null elements.然后结果选择器必须处理空元素。 Fx.
外汇。
static void Main(string[] args)
{
var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };
var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });
foreach (var item in res)
Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
}
take look at this example看看这个例子
class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
public static void LeftOuterJoinExample()
{
Person magnus = new Person {ID = 1, FirstName = "Magnus", LastName = "Hedlund"};
Person terry = new Person {ID = 2, FirstName = "Terry", LastName = "Adams"};
Person charlotte = new Person {ID = 3, FirstName = "Charlotte", LastName = "Weiss"};
Person arlene = new Person {ID = 4, FirstName = "Arlene", LastName = "Huff"};
Pet barley = new Pet {Name = "Barley", Owner = terry};
Pet boots = new Pet {Name = "Boots", Owner = terry};
Pet whiskers = new Pet {Name = "Whiskers", Owner = charlotte};
Pet bluemoon = new Pet {Name = "Blue Moon", Owner = terry};
Pet daisy = new Pet {Name = "Daisy", Owner = magnus};
// Create two lists.
List<Person> people = new List<Person> {magnus, terry, charlotte, arlene};
List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy};
var query = from person in people
where person.ID == 4
join pet in pets on person equals pet.Owner into personpets
from petOrNull in personpets.DefaultIfEmpty()
select new { Person=person, Pet = petOrNull};
foreach (var v in query )
{
Console.WriteLine("{0,-15}{1}", v.Person.FirstName + ":", (v.Pet == null ? "Does not Exist" : v.Pet.Name));
}
}
// This code produces the following output:
//
// Magnus: Daisy
// Terry: Barley
// Terry: Boots
// Terry: Blue Moon
// Charlotte: Whiskers
// Arlene:
now you are able to include elements from the left
even if that element has no matches in the right
, in our case we retrived Arlene
even he has no matching in the right现在您可以
include elements from the left
即使该元素has no matches in the right
项,在我们的例子中,我们检索了Arlene
,即使他在右侧没有匹配项
here is the reference这是参考
How to: Perform Left Outer Joins (C# Programming Guide)如何:执行左外连接(C# 编程指南)
This is the general form (as already provided in other answers)这是一般形式(已在其他答案中提供)
var c =
from a in alpha
join b in beta on b.field1 equals a.field1 into b_temp
from b_value in b_temp.DefaultIfEmpty()
select new { Alpha = a, Beta = b_value };
However here's an explanation that I hope will clarify what this actually means!然而,这里有一个解释,我希望能澄清这实际上意味着什么!
join b in beta on b.field1 equals a.field1 into b_temp
essentially creates a separate result set b_temp that effectively includes null 'rows' for entries on the right hand side (entries in 'b').本质上创建了一个单独的结果集 b_temp,它有效地包括右侧条目的空“行”(“b”中的条目)。
Then the next line:然后是下一行:
from b_value in b_temp.DefaultIfEmpty()
..iterates over that result set, setting the default null value for the 'row' on the right hand side, and setting the result of the right hand side row join to the value of 'b_value' (ie the value that's on the right hand side,if there's a matching record, or 'null' if there isn't). ..迭代该结果集,为右侧的“行”设置默认空值,并将右侧行连接的结果设置为“b_value”的值(即右侧的值手边,如果有匹配的记录,如果没有,则为“null”)。
Now, if the right hand side is the result of a separate LINQ query, it will consist of anonymous types, which can only either be 'something' or 'null'.现在,如果右侧是单独的 LINQ 查询的结果,它将由匿名类型组成,只能是“某物”或“空”。 If it's an enumerable however (eg a List - where MyObjectB is a class with 2 fields), then it's possible to be specific about what default 'null' values are used for its properties:
但是,如果它是可枚举的(例如 List - 其中 MyObjectB 是具有 2 个字段的类),则可以具体说明其属性使用的默认“null”值:
var c =
from a in alpha
join b in beta on b.field1 equals a.field1 into b_temp
from b_value in b_temp.DefaultIfEmpty( new MyObjectB { Field1 = String.Empty, Field2 = (DateTime?) null })
select new { Alpha = a, Beta_field1 = b_value.Field1, Beta_field2 = b_value.Field2 };
This ensures that 'b' itself isn't null (but its properties can be null, using the default null values that you've specified), and this allows you to check properties of b_value without getting a null reference exception for b_value.这确保了 'b' 本身不为空(但它的属性可以为空,使用您指定的默认空值),这允许您检查 b_value 的属性,而不会获得 b_value 的空引用异常。 Note that for a nullable DateTime, a type of (DateTime?) ie 'nullable DateTime' must be specified as the 'Type' of the null in the specification for the 'DefaultIfEmpty' (this will also apply to types that are not 'natively' nullable eg double, float).
请注意,对于可为空的 DateTime,(DateTime?)类型,即“可为空的 DateTime”,必须在“DefaultIfEmpty”的规范中指定为 null 的“类型”(这也适用于不是“本机”的类型' 可以为空,例如 double、float)。
You can perform multiple left outer joins by simply chaining the above syntax.您可以通过简单地链接上述语法来执行多个左外连接。
Here's an example if you need to join more than 2 tables:如果您需要加入 2 个以上的表,以下是一个示例:
from d in context.dc_tpatient_bookingd
join bookingm in context.dc_tpatient_bookingm
on d.bookingid equals bookingm.bookingid into bookingmGroup
from m in bookingmGroup.DefaultIfEmpty()
join patient in dc_tpatient
on m.prid equals patient.prid into patientGroup
from p in patientGroup.DefaultIfEmpty()
Ref: https://stackoverflow.com/a/17142392/2343参考: https ://stackoverflow.com/a/17142392/2343
Here is a fairly easy to understand version using method syntax:这是一个使用方法语法的相当容易理解的版本:
IEnumerable<JoinPair> outerLeft =
lefts.SelectMany(l =>
rights.Where(r => l.Key == r.Key)
.DefaultIfEmpty(new Item())
.Select(r => new JoinPair { LeftId = l.Id, RightId = r.Id }));
Extension method that works like left join with Join syntax扩展方法,类似于使用 Join 语法的左连接
public static class LinQExtensions
{
public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
this IEnumerable<TOuter> outer, IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector)
{
return outer.GroupJoin(
inner,
outerKeySelector,
innerKeySelector,
(outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault()));
}
}
just wrote it in .NET core and it seems to be working as expected.刚刚在 .NET 核心中编写它,它似乎按预期工作。
Small test:小测试:
var Ids = new List<int> { 1, 2, 3, 4};
var items = new List<Tuple<int, string>>
{
new Tuple<int, string>(1,"a"),
new Tuple<int, string>(2,"b"),
new Tuple<int, string>(4,"d"),
new Tuple<int, string>(5,"e"),
};
var result = Ids.LeftJoin(
items,
id => id,
item => item.Item1,
(id, item) => item ?? new Tuple<int, string>(id, "not found"));
result.ToList()
Count = 4
[0]: {(1, a)}
[1]: {(2, b)}
[2]: {(3, not found)}
[3]: {(4, d)}
I would like to add that if you get the MoreLinq extension there is now support for both homogenous and heterogeneous left joins now我想补充一点,如果您获得了 MoreLinq 扩展,现在现在支持同质和异构左连接
http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm
example:例子:
//Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them
return DataContext.ClientCompany
.LeftJoin(DataContext.Employees, //Table being joined
company => company.ClientCompanyID, //First key
employee => employee.ClientCompanyID, //Second Key
company => new {company, employee = (Employee)null}, //Result selector when there isn't a match
(company, employee) => new { company, employee }); //Result selector when there is a match
EDIT:编辑:
In retrospect this may work, but it converts the IQueryable to an IEnumerable as morelinq does not convert the query to SQL.回想起来,这可能有效,但它将 IQueryable 转换为 IEnumerable,因为 morelinq 不会将查询转换为 SQL。
You can instead use a GroupJoin as described here: https://stackoverflow.com/a/24273804/4251433您可以改为使用 GroupJoin,如下所述: https ://stackoverflow.com/a/24273804/4251433
This will ensure that it stays as an IQueryable in case you need to do further logical operations on it later.这将确保它保持为 IQueryable,以防您稍后需要对其进行进一步的逻辑操作。
There are three tables: persons, schools and persons_schools, which connects persons to the schools they study in. A reference to the person with id=6 is absent in the table persons_schools.共有三个表:persons、schools 和 persons_schools,将人员与他们就读的学校联系起来。persons_schools 表中没有对 id=6 的人员的引用。 However the person with id=6 is presented in the result lef-joined grid.
然而,id=6 的人显示在结果左连接网格中。
List<Person> persons = new List<Person>
{
new Person { id = 1, name = "Alex", phone = "4235234" },
new Person { id = 2, name = "Bob", phone = "0014352" },
new Person { id = 3, name = "Sam", phone = "1345" },
new Person { id = 4, name = "Den", phone = "3453452" },
new Person { id = 5, name = "Alen", phone = "0353012" },
new Person { id = 6, name = "Simon", phone = "0353012" }
};
List<School> schools = new List<School>
{
new School { id = 1, name = "Saint. John's school"},
new School { id = 2, name = "Public School 200"},
new School { id = 3, name = "Public School 203"}
};
List<PersonSchool> persons_schools = new List<PersonSchool>
{
new PersonSchool{id_person = 1, id_school = 1},
new PersonSchool{id_person = 2, id_school = 2},
new PersonSchool{id_person = 3, id_school = 3},
new PersonSchool{id_person = 4, id_school = 1},
new PersonSchool{id_person = 5, id_school = 2}
//a relation to the person with id=6 is absent
};
var query = from person in persons
join person_school in persons_schools on person.id equals person_school.id_person
into persons_schools_joined
from person_school_joined in persons_schools_joined.DefaultIfEmpty()
from school in schools.Where(var_school => person_school_joined == null ? false : var_school.id == person_school_joined.id_school).DefaultIfEmpty()
select new { Person = person.name, School = school == null ? String.Empty : school.name };
foreach (var elem in query)
{
System.Console.WriteLine("{0},{1}", elem.Person, elem.School);
}
Easy way is to use Let keyword.简单的方法是使用 Let 关键字。 This works for me.
这对我有用。
from AItem in Db.A
Let BItem = Db.B.Where(x => x.id == AItem.id ).FirstOrDefault()
Where SomeCondition
Select new YourViewModel
{
X1 = AItem.a,
X2 = AItem.b,
X3 = BItem.c
}
This is a simulation of Left Join.这是Left Join的模拟。 If each item in B table not match to A item , BItem return null
如果 B 表中的每个项目都与 A 项目不匹配,则 BItem 返回 null
This is a SQL syntax compare to LINQ syntax for inner and left outer joins.这是一种 SQL 语法,与用于内连接和左外连接的 LINQ 语法相比。 Left Outer Join:
左外连接:
http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html
"The following example does a group join between product and category. This is essentially the left join. The into expression returns data even if the category table is empty. To access the properties of the category table, we must now select from the enumerable result by adding the from cl in catList.DefaultIfEmpty() statement. “下面的例子在产品和类别之间进行组连接。这本质上是左连接。即使类别表为空,into 表达式也会返回数据。要访问类别表的属性,我们现在必须从可枚举的结果中进行选择通过在 catList.DefaultIfEmpty() 语句中添加 from cl。
As per my answer to a similar question, here:根据我对类似问题的回答,在这里:
Linq to SQL left outer join using Lambda syntax and joining on 2 columns (composite join key) Linq to SQL 使用 Lambda 语法左外连接并连接 2 列(复合连接键)
Get the code here , or clone my github repo , and play!在这里获取代码,或者克隆我的 github 仓库,然后玩!
Query:询问:
var petOwners =
from person in People
join pet in Pets
on new
{
person.Id,
person.Age,
}
equals new
{
pet.Id,
Age = pet.Age * 2, // owner is twice age of pet
}
into pets
from pet in pets.DefaultIfEmpty()
select new PetOwner
{
Person = person,
Pet = pet,
};
Lambda:拉姆达:
var petOwners = People.GroupJoin(
Pets,
person => new { person.Id, person.Age },
pet => new { pet.Id, Age = pet.Age * 2 },
(person, pet) => new
{
Person = person,
Pets = pet,
}).SelectMany(
pet => pet.Pets.DefaultIfEmpty(),
(people, pet) => new
{
people.Person,
Pet = pet,
});
Perform left outer joins in linq C# // Perform left outer joins在 linq C# 中执行左外连接 // 执行左外连接
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Child
{
public string Name { get; set; }
public Person Owner { get; set; }
}
public class JoinTest
{
public static void LeftOuterJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Child barley = new Child { Name = "Barley", Owner = terry };
Child boots = new Child { Name = "Boots", Owner = terry };
Child whiskers = new Child { Name = "Whiskers", Owner = charlotte };
Child bluemoon = new Child { Name = "Blue Moon", Owner = terry };
Child daisy = new Child { Name = "Daisy", Owner = magnus };
// Create two lists.
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
List<Child> childs = new List<Child> { barley, boots, whiskers, bluemoon, daisy };
var query = from person in people
join child in childs
on person equals child.Owner into gj
from subpet in gj.DefaultIfEmpty()
select new
{
person.FirstName,
ChildName = subpet!=null? subpet.Name:"No Child"
};
// PetName = subpet?.Name ?? String.Empty };
foreach (var v in query)
{
Console.WriteLine($"{v.FirstName + ":",-25}{v.ChildName}");
}
}
// This code produces the following output:
//
// Magnus: Daisy
// Terry: Barley
// Terry: Boots
// Terry: Blue Moon
// Charlotte: Whiskers
// Arlene: No Child
https://dotnetwithhamid.blogspot.in/ https://dotnetwithhamid.blogspot.in/
Here's a version of the extension method solution using IQueryable instead of IEnumerable这是使用 IQueryable 而不是 IEnumerable 的扩展方法解决方案的一个版本
public class OuterJoinResult<TLeft, TRight>
{
public TLeft LeftValue { get; set; }
public TRight RightValue { get; set; }
}
public static IQueryable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IQueryable<TLeft> left, IQueryable<TRight> right, Expression<Func<TLeft, TKey>> leftKey, Expression<Func<TRight, TKey>> rightKey, Expression<Func<OuterJoinResult<TLeft, TRight>, TResult>> result)
{
return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
.SelectMany(o => o.r.DefaultIfEmpty(), (l, r) => new OuterJoinResult<TLeft, TRight> { LeftValue = l.l, RightValue = r })
.Select(result);
}
If you need to join and filter on something, that can be done outside of the join.如果您需要加入并过滤某些内容,可以在加入之外完成。 Filter can be done after creating the collection.
可以在创建集合后进行过滤。
In this case if I do this in the join condition I reduce the rows that are returned.在这种情况下,如果我在连接条件中执行此操作,我会减少返回的行。
Ternary condition is used (= n == null ? "__" : n.MonDayNote,)
使用三元条件
(= n == null ? "__" : n.MonDayNote,)
If the object is null
(so no match), then return what is after the ?
如果对象为
null
(因此不匹配),则返回 ? 之后的内容?
. .
__
, in this case. __
,在这种情况下。
Else, return what is after the :
, n.MonDayNote
.否则,返回
:
、 n.MonDayNote
之后的内容。
Thanks to the other contributors that is where I started with my own issue.感谢其他贡献者,这是我从自己的问题开始的地方。
var schedLocations = (from f in db.RAMS_REVENUE_LOCATIONS
join n in db.RAMS_LOCATION_PLANNED_MANNING on f.revenueCenterID equals
n.revenueCenterID into lm
from n in lm.DefaultIfEmpty()
join r in db.RAMS_LOCATION_SCHED_NOTE on f.revenueCenterID equals r.revenueCenterID
into locnotes
from r in locnotes.DefaultIfEmpty()
where f.LocID == nLocID && f.In_Use == true && f.revenueCenterID > 1000
orderby f.Areano ascending, f.Locname ascending
select new
{
Facname = f.Locname,
f.Areano,
f.revenueCenterID,
f.Locabbrev,
// MonNote = n == null ? "__" : n.MonDayNote,
MonNote = n == null ? "__" : n.MonDayNote,
TueNote = n == null ? "__" : n.TueDayNote,
WedNote = n == null ? "__" : n.WedDayNote,
ThuNote = n == null ? "__" : n.ThuDayNote,
FriNote = n == null ? "__" : n.FriDayNote,
SatNote = n == null ? "__" : n.SatDayNote,
SunNote = n == null ? "__" : n.SunDayNote,
MonEmpNbr = n == null ? 0 : n.MonEmpNbr,
TueEmpNbr = n == null ? 0 : n.TueEmpNbr,
WedEmpNbr = n == null ? 0 : n.WedEmpNbr,
ThuEmpNbr = n == null ? 0 : n.ThuEmpNbr,
FriEmpNbr = n == null ? 0 : n.FriEmpNbr,
SatEmpNbr = n == null ? 0 : n.SatEmpNbr,
SunEmpNbr = n == null ? 0 : n.SunEmpNbr,
SchedMondayDate = n == null ? dMon : n.MondaySchedDate,
LocNotes = r == null ? "Notes: N/A" : r.LocationNote
}).ToList();
Func<int, string> LambdaManning = (x) => { return x == 0 ? "" : "Manning:" + x.ToString(); };
DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"];
var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon);
class Program
{
List<Employee> listOfEmp = new List<Employee>();
List<Department> listOfDepart = new List<Department>();
public Program()
{
listOfDepart = new List<Department>(){
new Department { Id = 1, DeptName = "DEV" },
new Department { Id = 2, DeptName = "QA" },
new Department { Id = 3, DeptName = "BUILD" },
new Department { Id = 4, DeptName = "SIT" }
};
listOfEmp = new List<Employee>(){
new Employee { Empid = 1, Name = "Manikandan",DepartmentId=1 },
new Employee { Empid = 2, Name = "Manoj" ,DepartmentId=1},
new Employee { Empid = 3, Name = "Yokesh" ,DepartmentId=0},
new Employee { Empid = 3, Name = "Purusotham",DepartmentId=0}
};
}
static void Main(string[] args)
{
Program ob = new Program();
ob.LeftJoin();
Console.ReadLine();
}
private void LeftJoin()
{
listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach
(z =>
{
Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt);
});
}
}
class Employee
{
public int Empid { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
}
class Department
{
public int Id { get; set; }
public string DeptName { get; set; }
}
Overview: In this code snippet, I demonstrate how to group by ID where Table1 and Table2 have a one to many relationship.概述:在此代码段中,我演示了如何在 Table1 和 Table2 具有一对多关系的情况下按 ID 进行分组。 I group on Id, Field1, and Field2.
我对 Id、Field1 和 Field2 进行分组。 The subquery is helpful, if a third Table lookup is required and it would have required a left join relationship.
如果需要第三次表查找并且它需要左连接关系,则子查询很有帮助。 I show a left join grouping and a subquery linq.
我展示了一个左连接分组和一个子查询 linq。 The results are equivalent.
结果是等价的。
class MyView
{
public integer Id {get,set};
public String Field1 {get;set;}
public String Field2 {get;set;}
public String SubQueryName {get;set;}
}
IList<MyView> list = await (from ci in _dbContext.Table1
join cii in _dbContext.Table2
on ci.Id equals cii.Id
where ci.Field1 == criterion
group new
{
ci.Id
} by new { ci.Id, cii.Field1, ci.Field2}
into pg
select new MyView
{
Id = pg.Key.Id,
Field1 = pg.Key.Field1,
Field2 = pg.Key.Field2,
SubQueryName=
(from chv in _dbContext.Table3 where chv.Id==pg.Key.Id select chv.Field1).FirstOrDefault()
}).ToListAsync<MyView>();
Compared to using a Left Join and Group new
IList<MyView> list = await (from ci in _dbContext.Table1
join cii in _dbContext.Table2
on ci.Id equals cii.Id
join chv in _dbContext.Table3
on cii.Id equals chv.Id into lf_chv
from chv in lf_chv.DefaultIfEmpty()
where ci.Field1 == criterion
group new
{
ci.Id
} by new { ci.Id, cii.Field1, ci.Field2, chv.FieldValue}
into pg
select new MyView
{
Id = pg.Key.Id,
Field1 = pg.Key.Field1,
Field2 = pg.Key.Field2,
SubQueryName=pg.Key.FieldValue
}).ToListAsync<MyView>();
The prescribed way to do a left join using LINQ is to to a GroupJoin
followed by a SelectMany
.使用 LINQ 进行左连接的规定方法是到
GroupJoin
后跟SelectMany
。 I prefer to abstract this logic into a LeftJoin
extension method to make the code much more readable.我更喜欢将此逻辑抽象为
LeftJoin
扩展方法,以使代码更具可读性。
Here is the LeftJoin
implementation that I use.这是我使用的
LeftJoin
实现。 The two major benefits of this implementation are:此实施的两个主要好处是:
It specifically works with IQueryable
s它特别适用于
IQueryable
s
It has the exact same parameter and return type as the Join
method:它具有与
Join
方法完全相同的参数和返回类型:
var results = DbContext.Categories.LeftJoin( DbContext.Products, c => c.Id, p => p.CategoryId, (c, p) => new { Category = c, ProductName = p == null? "(No Products)": p.ProductName }).ToList(); var results = DbContext.Categories.Join( DbContext.Products, c => c.Id, p => p.CategoryId, (c, p) => new { Category = c, ProductName = p.ProductName }).ToList();
Here's how:就是这样:
IQueryable Extensions: IQueryable 扩展:
public static class QueryableExtensions
{
public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector)
{
var query = outer
.GroupJoin(inner, outerKeySelector, innerKeySelector, (o, i) => new { o, i })
.SelectMany(o => o.i.DefaultIfEmpty(), (x, i) => new { x.o, i });
return ApplySelector(query, x => x.o, x => x.i, resultSelector);
}
private static IQueryable<TResult> ApplySelector<TSource, TOuter, TInner, TResult>(
IQueryable<TSource> source,
Expression<Func<TSource, TOuter>> outerProperty,
Expression<Func<TSource, TInner>> innerProperty,
Expression<Func<TOuter, TInner, TResult>> resultSelector)
{
var p = Expression.Parameter(typeof(TSource), $"param_{Guid.NewGuid()}".Replace("-", string.Empty));
Expression body = resultSelector?.Body
.ReplaceParameter(resultSelector.Parameters[0], outerProperty.Body.ReplaceParameter(outerProperty.Parameters[0], p))
.ReplaceParameter(resultSelector.Parameters[1], innerProperty.Body.ReplaceParameter(innerProperty.Parameters[0], p));
var selector = Expression.Lambda<Func<TSource, TResult>>(body, p);
return source.Select(selector);
}
}
Expression Extensions:表达式扩展:
public static class ExpressionExtensions
{
public static Expression ReplaceParameter(this Expression source, ParameterExpression toReplace, Expression newExpression)
=> new ReplaceParameterExpressionVisitor(toReplace, newExpression).Visit(source);
}
Expression Visitors:表达访客:
public class ReplaceParameterExpressionVisitor : ExpressionVisitor
{
public ReplaceParameterExpressionVisitor(ParameterExpression toReplace, Expression replacement)
{
this.ToReplace = toReplace;
this.Replacement = replacement;
}
public ParameterExpression ToReplace { get; }
public Expression Replacement { get; }
protected override Expression VisitParameter(ParameterExpression node)
=> (node == ToReplace) ? Replacement : base.VisitParameter(node);
}
This is the prettiest solution I use, give it a try!这是我使用的最漂亮的解决方案,试一试!
(from c in categories
let product = products.Where(d=> d.Category == c.Category).FirstOrDefault()
select new { Category = c, ProductName = p == null ? "(No products)" : product.ProductName };
(from a in db.Assignments
join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId
//from d in eGroup.DefaultIfEmpty()
join c in db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2
from e in eGroup2.DefaultIfEmpty()
where (a.Collected == false)
select new
{
OrderId = a.OrderId,
DeliveryBoyID = a.AssignTo,
AssignedBoyName = b.Name,
Assigndate = a.Assigndate,
Collected = a.Collected,
CollectedDate = a.CollectedDate,
CollectionBagNo = a.CollectionBagNo,
DeliverTo = e == null ? "Null" : e.Name,
DeliverDate = a.DeliverDate,
DeliverBagNo = a.DeliverBagNo,
Delivered = a.Delivered
});
Simple solution for the LEFT OUTER JOIN : LEFT OUTER JOIN 的简单解决方案:
var setA = context.SetA;
var setB = context.SetB.Select(st=>st.Id).Distinct().ToList();
var leftOuter = setA.Where(stA=> !setB.Contains(stA.Id));
notes :笔记:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.