@@ -768,6 +768,26 @@ from o in c.Orders
768768 }
769769 }
770770
771+ [ Category ( "JOIN" ) ]
772+ [ Test ( Description = "This sample uses foreign key navigation in the " +
773+ "from clause to select all orders for customers in London." ) ]
774+ public async Task DLinqJoin1LeftJoinAsync ( )
775+ {
776+ IQueryable < Order > q =
777+ from c in db . Customers
778+ from o in c . Orders . DefaultIfEmpty ( )
779+ where c . Address . City == "London"
780+ select o ;
781+
782+ using ( var sqlSpy = new SqlLogSpy ( ) )
783+ {
784+ await ( ObjectDumper . WriteAsync ( q ) ) ;
785+
786+ var sql = sqlSpy . GetWholeLog ( ) ;
787+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 1 ) ) ;
788+ }
789+ }
790+
771791 [ Category ( "JOIN" ) ]
772792 [ Test ( Description = "This sample shows how to construct a join where one side is nullable and the other isn't." ) ]
773793 public async Task DLinqJoin10Async ( )
@@ -974,6 +994,26 @@ join o in db.Orders on c.CustomerId equals o.Customer.CustomerId
974994 }
975995 }
976996
997+ [ Category ( "JOIN" ) ]
998+ [ Test ( Description = "This sample explictly joins two tables and projects results from both tables." ) ]
999+ public async Task DLinqJoin5aLeftJoinAsync ( )
1000+ {
1001+ var q =
1002+ from c in db . Customers
1003+ join o in db . Orders on c . CustomerId equals o . Customer . CustomerId into orders
1004+ from o in orders . DefaultIfEmpty ( )
1005+ where o != null
1006+ select new { c . ContactName , o . OrderId } ;
1007+
1008+ using ( var sqlSpy = new SqlLogSpy ( ) )
1009+ {
1010+ await ( ObjectDumper . WriteAsync ( q ) ) ;
1011+
1012+ var sql = sqlSpy . GetWholeLog ( ) ;
1013+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 1 ) ) ;
1014+ }
1015+ }
1016+
9771017 [ Category ( "JOIN" ) ]
9781018 [ Test ( Description = "This sample explictly joins two tables and projects results from both tables using a group join." ) ]
9791019 public async Task DLinqJoin5bAsync ( )
@@ -1032,6 +1072,21 @@ join o in db.Orders on
10321072 }
10331073 }
10341074
1075+ [ Category ( "JOIN" ) ]
1076+ [ Test ( Description = "This sample explictly joins two tables with a composite key and projects results from both tables." ) ]
1077+ public void DLinqJoin5dLeftJoinAsync ( )
1078+ {
1079+ var q =
1080+ from c in db . Customers
1081+ join o in db . Orders on
1082+ new { c . CustomerId , HasContractTitle = c . ContactTitle != null } equals
1083+ new { o . Customer . CustomerId , HasContractTitle = o . Customer . ContactTitle != null } into orders
1084+ from o in orders . DefaultIfEmpty ( )
1085+ select new { c . ContactName , o . OrderId } ;
1086+
1087+ Assert . ThrowsAsync < NotSupportedException > ( ( ) => ObjectDumper . WriteAsync ( q ) ) ;
1088+ }
1089+
10351090 [ Category ( "JOIN" ) ]
10361091 [ Test ( Description = "This sample joins two tables and projects results from the first table." ) ]
10371092 public async Task DLinqJoin5eAsync ( )
@@ -1051,6 +1106,26 @@ join o in db.Orders on c.CustomerId equals o.Customer.CustomerId
10511106 }
10521107 }
10531108
1109+ [ Category ( "JOIN" ) ]
1110+ [ Test ( Description = "This sample joins two tables and projects results from the first table." ) ]
1111+ public async Task DLinqJoin5eLeftJoinAsync ( )
1112+ {
1113+ var q =
1114+ from c in db . Customers
1115+ join o in db . Orders on c . CustomerId equals o . Customer . CustomerId into orders
1116+ from o in orders . DefaultIfEmpty ( )
1117+ where c . ContactName != null
1118+ select o ;
1119+
1120+ using ( var sqlSpy = new SqlLogSpy ( ) )
1121+ {
1122+ await ( ObjectDumper . WriteAsync ( q ) ) ;
1123+
1124+ var sql = sqlSpy . GetWholeLog ( ) ;
1125+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 1 ) ) ;
1126+ }
1127+ }
1128+
10541129 [ Category ( "JOIN" ) ]
10551130 [ TestCase ( Description = "This sample explictly joins two tables with a composite key and projects results from both tables." ) ]
10561131 public async Task DLinqJoin5fAsync ( )
@@ -1073,21 +1148,24 @@ join c in db.Customers on
10731148 }
10741149
10751150 [ Category ( "JOIN" ) ]
1076- [ Test ( Description = "This sample joins two tables and projects results from the first table ." ) ]
1077- public async Task DLinqJoin5eAsync ( )
1151+ [ TestCase ( Description = "This sample explictly joins two tables with a composite key and projects results from both tables ." ) ]
1152+ public async Task DLinqJoin5fLeftJoinAsync ( )
10781153 {
10791154 var q =
1080- from c in db . Customers
1081- join o in db . Orders on c . CustomerId equals o . Customer . CustomerId
1082- where c . ContactName != null
1083- select o ;
1155+ from o in db . Orders
1156+ join c in db . Customers on
1157+ new { o . Customer . CustomerId , HasContractTitle = o . Customer . ContactTitle != null } equals
1158+ new { c . CustomerId , HasContractTitle = c . ContactTitle != null } into customers
1159+ from c in customers . DefaultIfEmpty ( )
1160+ select new { c . ContactName , o . OrderId } ;
10841161
10851162 using ( var sqlSpy = new SqlLogSpy ( ) )
10861163 {
10871164 await ( ObjectDumper . WriteAsync ( q ) ) ;
10881165
10891166 var sql = sqlSpy . GetWholeLog ( ) ;
1090- Assert . That ( GetTotalOccurrences ( sql , "inner join" ) , Is . EqualTo ( 1 ) ) ;
1167+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 2 ) ) ;
1168+ Assert . That ( GetTotalOccurrences ( sql , "inner join" ) , Is . EqualTo ( 0 ) ) ;
10911169 }
10921170 }
10931171
@@ -1113,6 +1191,28 @@ join e in db.Employees on c.Address.City equals e.Address.City into emps
11131191 }
11141192 }
11151193
1194+ [ Category ( "JOIN" ) ]
1195+ [ Test (
1196+ Description =
1197+ "This sample shows how to get LEFT OUTER JOIN by using DefaultIfEmpty(). The DefaultIfEmpty() method returns null when there is no Order for the Employee."
1198+ ) ]
1199+ public async Task DLinqJoin7Async ( )
1200+ {
1201+ var q =
1202+ from e in db . Employees
1203+ join o in db . Orders on e equals o . Employee into ords
1204+ from o in ords . DefaultIfEmpty ( )
1205+ select new { e . FirstName , e . LastName , Order = o } ;
1206+
1207+ using ( var sqlSpy = new SqlLogSpy ( ) )
1208+ {
1209+ await ( ObjectDumper . WriteAsync ( q ) ) ;
1210+
1211+ var sql = sqlSpy . GetWholeLog ( ) ;
1212+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 1 ) ) ;
1213+ }
1214+ }
1215+
11161216 [ Category ( "JOIN" ) ]
11171217 [ Test ( Description = "This sample projects a 'let' expression resulting from a join." ) ]
11181218 public async Task DLinqJoin8Async ( )
@@ -1175,6 +1275,51 @@ from d in details
11751275 }
11761276 }
11771277
1278+ [ Category ( "JOIN" ) ]
1279+ [ TestCase ( true , Description = "This sample shows a group left join with a composite key." ) ]
1280+ [ TestCase ( false , Description = "This sample shows a group left join with a composite key." ) ]
1281+ public async Task DLinqJoin9LeftJoinAsync ( bool useCrossJoin )
1282+ {
1283+ if ( useCrossJoin && ! Dialect . SupportsCrossJoin )
1284+ {
1285+ Assert . Ignore ( "Dialect does not support cross join." ) ;
1286+ }
1287+
1288+ ICollection expected , actual ;
1289+ expected =
1290+ ( from o in db . Orders . ToList ( )
1291+ from p in db . Products . ToList ( )
1292+ join d in db . OrderLines . ToList ( )
1293+ on new { o . OrderId , p . ProductId } equals new { d . Order . OrderId , d . Product . ProductId }
1294+ into details
1295+ from d in details . DefaultIfEmpty ( )
1296+ where d != null && d . UnitPrice > 50
1297+ select new { o . OrderId , p . ProductId , d . UnitPrice } ) . ToList ( ) ;
1298+
1299+ using ( var substitute = SubstituteDialect ( ) )
1300+ using ( var sqlSpy = new SqlLogSpy ( ) )
1301+ {
1302+ ClearQueryPlanCache ( ) ;
1303+ substitute . Value . SupportsCrossJoin . Returns ( useCrossJoin ) ;
1304+
1305+ actual =
1306+ await ( ( from o in db . Orders
1307+ from p in db . Products
1308+ join d in db . OrderLines
1309+ on new { o . OrderId , p . ProductId } equals new { d . Order . OrderId , d . Product . ProductId }
1310+ into details
1311+ from d in details . DefaultIfEmpty ( )
1312+ where d != null && d . UnitPrice > 50
1313+ select new { o . OrderId , p . ProductId , d . UnitPrice } ) . ToListAsync ( ) ) ;
1314+
1315+ var sql = sqlSpy . GetWholeLog ( ) ;
1316+ Assert . That ( sql , Does . Contain ( useCrossJoin ? "cross join" : "inner join" ) ) ;
1317+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 1 ) ) ;
1318+ }
1319+
1320+ Assert . AreEqual ( expected . Count , actual . Count ) ;
1321+ }
1322+
11781323 [ Category ( "JOIN" ) ]
11791324 [ Test ( Description = "This sample shows a join which is then grouped" ) ]
11801325 public async Task DLinqJoin9bAsync ( )
@@ -1205,5 +1350,26 @@ join s2 in db.Employees on s.Superior.EmployeeId equals s2.EmployeeId
12051350 Assert . That ( GetTotalOccurrences ( sql , "inner join" ) , Is . EqualTo ( 2 ) ) ;
12061351 }
12071352 }
1353+
1354+ [ Category ( "JOIN" ) ]
1355+ [ Test ( Description = "This sample shows how to join multiple tables using a left join." ) ]
1356+ public async Task DLinqJoin10aLeftJoinAsync ( )
1357+ {
1358+ var q =
1359+ from e in db . Employees
1360+ join s in db . Employees on e . Superior . EmployeeId equals s . EmployeeId into sup
1361+ from s in sup . DefaultIfEmpty ( )
1362+ join s2 in db . Employees on s . Superior . EmployeeId equals s2 . EmployeeId into sup2
1363+ from s2 in sup2 . DefaultIfEmpty ( )
1364+ select new { e . FirstName , SuperiorName = s . FirstName , Superior2Name = s2 . FirstName } ;
1365+
1366+ using ( var sqlSpy = new SqlLogSpy ( ) )
1367+ {
1368+ await ( ObjectDumper . WriteAsync ( q ) ) ;
1369+
1370+ var sql = sqlSpy . GetWholeLog ( ) ;
1371+ Assert . That ( GetTotalOccurrences ( sql , "left outer join" ) , Is . EqualTo ( 2 ) ) ;
1372+ }
1373+ }
12081374 }
12091375}
0 commit comments