Issue On The Linq Query - linq

I am trying to do a very simple task with Linq and already stuck. It's really horrible but I guess, it's better to know using this post. I've two tables. One is Products and another is Ratings. Here is the demo script:
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) PRIMARY KEY,
[ProductName] [varchar](40) NULL,
[Price] [float] NULL,
[Details] [varchar](max) NULL,
[CategoryID] [int] NULL
)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (1, N'Denim', 1200, NULL, 1)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (2, N'Denim 2', 220, NULL, 1)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (3, N'Pringles', 240, NULL, 2)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (4, N'Pringles 2', 260, NULL, 2)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (5, N'Pringles 3', 240, NULL, 2)
CREATE TABLE [dbo].[Ratings](
[AutoId] [int] IDENTITY(1,1) PRIMARY KEY,
[ProductId] [int] NOT NULL,
[UserRating] [float] NOT NULL
)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (4, 2, 1.5)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (5, 4, 2.5)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (6, 1, 5)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (7, 2, 2.5)
And the output should be the following:
ProductId - ProductName - User Rating
1 - Denim - 5
2 - Denim 2 - 2
3 - Pringles - 0
4 - Pringles 2 - 2.5
5 - Pringles 3 - 0
This is the rating system of a project and I am trying to get the above output using Linq. With the following Sql, I got the result:
SELECT m.ProductId, m.ProductName, ISNULL(SUM(k.UserRating) / COUNT(k.ProductId), 0) AS 'User Rating' FROM Products m
LEFT JOIN Ratings k ON k.ProductId = m.ProductID
GROUP BY m.ProductID, m.ProductName
Unfortunately, with the following Linq, I get only the rating details that exist in the Ratings table:
var con = (from c in db.Ratings
join d in db.Products on c.ProductId equals d.ProductID into ps
from rt in ps.DefaultIfEmpty()
group new { c, ps } by new { rt.ProductID, rt.ProductName } into g
select new ProductRating
{
ProductId = g.Key.ProductID,
ProductName = g.Key.ProductName,
Total = g.Sum(c => c.c.UserRating) / g.Count()
}).ToList();
Note: My goal is to get the rating details (sum of a product's rating) that don't exist in the Ratings table (Should return '0' if no data) along with the ratings details that exist.
Update 1 - Class ProductRating:
public class ProductRating {
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Total { get; set; } //Property for the rating
}
Update 2 - Class Products and Ratings:
public partial class Products
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public Nullable<double> Price { get; set; }
public string Details { get; set; }
public Nullable<int> CategoryID { get; set; }
public ICollection<Ratings> Rating { get; set; }
}
public partial class Ratings
{
public int AutoId { get; set; }
public int ProductId { get; set; }
public double UserRating { get; set; }
}
Used the following the Linq query and got this error - The specified type member 'Rating' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported:
var con = db.Products.Select(c => new ProductRating
{
ProductId = c.ProductID,
ProductName = c.ProductName,
Total = c.Rating.Average(d => (double?)d.UserRating) ?? 0
}).ToList();
Update 3: Now getting this error - error 2016: The value specified for the condition is not compatible with the type of the member and this is how I tried for the navigation property:
Note: I've modified Andrés Robinet's query and it worked though using the Join.

from c in db.Ratings
join d in db.Products on c.ProductId equals d.ProductID into ps
from rt in ps.DefaultIfEmpty()
is the LINQ equivalent of the SQL Ratings LEFT OUTER JOIN Products, i.e. exactly the opposite of your SQL query.
While the LINQ query can be modified to match the SQL query, it doesn't make sense because EF provides a much better way which totally eliminates the need of using joins in the LINQ query - the so called navigation properties (see Don’t use Linq’s Join. Navigate!).
Normally the Product class would have a collection navigation property
public ICollection<Rating> Ratings { get; set; }
representing the one to many relationship between Product and Rating, and the equivalent LINQ query using it would be simply:
var result = db.Products
.Select(p => new ProductRating
{
ProductId = p.ProductId,
ProductName = p.ProductName,
Total = p.Ratings.Average(r => (double?)r.UserRating) ?? 0
}).ToList();
Update: In case you haven't set up correctly the relationship in the entity model, you could use the manual join equivalent of the above query by replacing the navigation property accessor with group join:
var result = (
from p in db.Products
join r in db.Ratings on p.ProductId equals r.ProductId into ratings
select new ProductRating
{
ProductId = p.ProductId,
ProductName = p.ProductName,
Total = ratings.Average(r => (double?)r.UserRating) ?? 0
}).ToList();

You have to start with the Product. Also, the order of your joins in your Linq expression is the opposite than in your SQL. And, the group ... by ... needs just rt.UserRating as the value selector
var con = (from d in db.Products
join c in db.Ratings on d.ProductId equals c.ProductId into ps
from rt in ps.DefaultIfEmpty()
group new { rt.UserRating } by new { d.ProductId, d.ProductName } into g
select new ProductRating
{
ProductId = g.Key.ProductId,
ProductName = g.Key.ProductName,
Total = g.Sum(r => r.UserRating) / g.Count()
})
.ToList();
Also, this would break if g.Count() is zero. But you have a few choices anyways:
Add more complexity to the query and pray for EF to be able to translate it into SQL
select new ProductRating
{
ProductName = g.Key.ProductName,
Total = g.Count() > 0 ? (g.Sum(r => r.UserRating) / g.Count()) : 0
}
Redefine ProductRating or use a helper DTO, so that it takes Sum and Count properties (calculate the average on-demand, let's say, you can still have a Total property)
select new ProductRating
{
ProductName = g.Key.ProductName,
SumRating = g.Sum(r => r.UserRating),
CountRating = g.Count()
}

Your LINQ statement has an inner join, whereas your SQL has a left join.

Related

Linq left outer join doesn't work while matching sql does [duplicate]

How to perform left outer join in C# LINQ to objects without using join-on-equals-into clauses? Is there any way to do that with where clause?
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:
public class JoinPair { long leftId; long rightId; }
As stated in "Perform left outer joins":
var q =
from c in categories
join pt in products on c.Category equals pt.Category into ps_jointable
from p in ps_jointable.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:
from c in categories
from p in products.Where(c == p.Category).DefaultIfEmpty()
If you omit the DefaultIfEmpty() you will have an inner join.
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.
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). Don't do this in Linq-2-Objects (because there's no DB-optimizer when you use Linq-to-Objects).
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:
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.
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...):
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
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:
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.
Take a look at this example.
This query should work:
var leftFinal = from left in lefts
join right in rights on left equals right.Left into leftRights
from leftRight in leftRights.DefaultIfEmpty()
select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };
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
here is the reference
How to: Perform Left Outer Joins (C# Programming Guide)
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').
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' (i.e. the value that's on the right hand side,if there's a matching record, or 'null' if there isn't).
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'. If it's an enumerable however (e.g. 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:
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. Note that for a nullable DateTime, a type of (DateTime?) i.e. '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 e.g 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:
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
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
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.
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
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.
You can instead use a GroupJoin as described here: 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.
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. However the person with id=6 is presented in the result lef-joined grid.
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. 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. If each item in B table not match to A item , BItem return null
This is a SQL syntax compare to LINQ syntax for inner and left outer joins.
Left Outer Join:
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.
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)
Get the code here, or clone my github repo, and play!
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,
});
This is the LeftJoin implementation I use. Notice that the the resultSelector expression accepts 2 parameters: one instance from both sides of the join. In most other implementations that I've seen the result selector only accepts one parameter, which is a "join model" with a left/right or outer/inner property. I like this implementation better because it has the same method signature as the built-in Join method. It also works with IQueryables and EF.
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();
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);
}
}
public static class ExpressionExtensions
{
public static Expression ReplaceParameter(this Expression source, ParameterExpression toReplace, Expression newExpression)
=> new ReplaceParameterExpressionVisitor(toReplace, newExpression).Visit(source);
}
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);
}
Perform left outer joins in linq C#
// Perform left outer joins
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/
Here's a version of the extension method solution using IQueryable instead of 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,)
If the object is null (so no match), then return what is after the ?. __, in this case.
Else, return what is after the :, 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; }
}
OUTPUT
Overview: In this code snippet, I demonstrate how to group by ID where Table1 and Table2 have a one to many relationship. I group on
Id, Field1, and 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. 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>();
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
});

EF Core LINQ use scalar function

I use Entity Framework Core 2.1.
I have a scalar function in the database which adds specified number of days.
I created an extension method to execute it:
public static class AdventureWorks2012ContextExt
{
public static DateTime? ExecFn_AddDayPeriod(this AdventureWorks2012Context db, DateTime dateTime, int days, string periodName)
{
var sql = $"set #result = dbo.[fn_AddDayPeriod]('{dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}', {days}, '{periodName}')";
var output = new SqlParameter { ParameterName = #"result", DbType = DbType.DateTime, Size = 16, Direction = ParameterDirection.Output };
var result = db.Database.ExecuteSqlCommand(sql, output);
return output.Value as DateTime?;
}
}
I try to use a scalar function in the query (to simplify things I use AdventureWorks2012) as follows:
var persons =
(from p in db.Person
join pa in db.Address on p.BusinessEntityId equals pa.AddressId
where p.ModifiedDate > db.ExecFn_AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")
select p).ToList();
But get an System.InvalidOperationException: 'A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.'
How can I achieve this?
UPDATE:
I managed to do it with the help of Ivan's answer:
var persons =
(from p in db.Person
join bea in db.BusinessEntityAddress on p.BusinessEntityId equals bea.BusinessEntityId
join a in db.Address on bea.AddressId equals a.AddressId
where p.ModifiedDate > AdventureWorks2012ContextFunctions.AddDayPeriod(a.ModifiedDate, 100, "DayPeriod_day")
select p).ToList();
But now I need to update ModifiedDate for filtered persons. So I'm doing like this:
var persons =
(from p in db.Person
join bea in db.BusinessEntityAddress on p.BusinessEntityId equals bea.BusinessEntityId
join a in db.Address on bea.AddressId equals a.AddressId
let date = AdventureWorks2012ContextFunctions.AddDayPeriod(a.ModifiedDate, 100, "DayPeriod_day")
where p.ModifiedDate > date
select new { Person = p, NewDate = date }).ToList();
foreach (var p in persons)
p.Person.ModifiedDate = p.NewDate ?? DateTime.Now;
db.SaveChanges();
But got System.NotSupportedException: 'Specified method is not supported.'
How can I use scalar function in select statement?
I tried to split the query by two parts:
var filteredPersons = // ok
(from p in db.Person
join bea in db.BusinessEntityAddress on p.BusinessEntityId equals bea.BusinessEntityId
join a in db.Address on bea.AddressId equals a.AddressId
where p.ModifiedDate > AdventureWorks2012ContextFunctions.AddDayPeriod(a.ModifiedDate, 100, "DayPeriod_day")
select new { Person = p, a.ModifiedDate }).ToList();
var persons = // here an exception occurs
(from p in filteredPersons
select new { Person = p, NewDate = AdventureWorks2012ContextFunctions.AddDayPeriod(p.ModifiedDate, 100, "DayPeriod_day") }).ToList();
Instead of invoking the function client side (which is this particular case happens as part of the client evaluation of the query filter, while the query reading is still in progress), you can use EF Core Database scalar function mapping so it
can be used in LINQ queries and translated to SQL.
One way to do that is to create a public static method in the derived context class and mark it with DbFunction attribute:
public partial class AdventureWorks2012Context
{
[DbFunction("fn_AddDayPeriod")]
public static DateTime? AddDayPeriod(DateTime dateTime, int days, string periodName) => throw new NotSupportedException();
}
and use
where p.ModifiedDate > AdventureWorks2012Context.AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")
Another way is to create a public static method in another class
public static class AdventureWorks2012DbFunctions
{
[DbFunction("fn_AddDayPeriod")]
public static DateTime? AddDayPeriod(DateTime dateTime, int days, string periodName) => throw new NotSupportedException();
}
but then you'll need to register it with fluent API (which happens automatically for methods defined inside the context derived class):
modelBuilder
.HasDbFunction(() => AdventureWorks2012DbFunctions.AddDayPeriod(default(DateTime), default(int), default(string)));
The usage is the same:
where p.ModifiedDate > AdventureWorksDbFunctions.AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")

How to match two Lists with only items that are different in Linq

I have a StudentData class
public class StudentData
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public int? Bonus { get; set; }
public int? Subject1Mark { get; set; }
public int? Subject2Mark { get; set; }
public int? Subject3Mark{ get; set; }
}
Each student has a unique Id that identifies him
I have a List<StudentData> CurrentData that has data of
1, John, Smith, 10, 50 ,50 ,50
2, Peter, Parker, 10, 60 ,60 ,60
3, Sally, Smart, 10, 70 ,70 ,70
4, Danny, Darko, 20, 80, 80, 80
I then have a List<StudentData> DataToUpdate which only contains the Id and Marks fields. Not the other fields.
1, null, null, null, 50 ,50 ,50
2, null, null, null, 65, 60 ,60
3, null, null, null, 70 ,70 ,70
The Ids of the list are not necessary in the same order
If you compare the two lists only Peter Parker's marks have changed in one subject.
I want to get the output to return
2, Peter, Parker, 10, 65 ,60 ,60
I want to takeList<StudentData> CurrentData inner join this with List<StudentData> DataToUpdate but only where marks are different
So in SQL it want the following
SELECT
CurrentData.Id,
CurrentData.FirstName ,
CurrentData.Surname,
CurrentData.Bonus,
DataToUpdate.Subject1Mark,
DataToUpdate.Subject2Mark,
DataToUpdate.Subject3Mark
FROM CurrentData
INNER JOIN DataToUpdate
ON CurrentData.Id= DataToUpdate.Id
AND (
CurrentData.Subject1Mark<> DataToUpdate.Subject1Mark
OR
CurrentData.Subject2Mark<> DataToUpdate.Subject2Mark
OR
CurrentData.Subject3Mark<> DataToUpdate.Subject3Mark
)
How do I do the above in LINQ?
In the Linq select how do I take all properties from CurrentData but include the 3 Subject properties from DataToUpdate in it to give me List<ChangedData>?
I could map each and every property but my StudentData has 100 fields and I would prefer to have something like
select new StudentData {
this=CurrentData,
this.Subject1Mark=DataToUpdate.Subject1Mark,
this.Subject2Mark=DataToUpdate.Subject2Mark,
this.Subject3Mark=DataToUpdate.Subject3Mark,
}
but I'm not sure how to write this
There is an answer in another stackoverflow question which should work but it doesn't. If I implement that solution (I simplify the example for simplicity)
var changedData = currentData
.Join(dataToUpdate, cd => cd.Id, ld => ld.Id, (cd, ld) => new { cd, ld })
.Select(x => { x.cd.Subject1Mark= x.ld.Subject1Mark; return x.cd; })
;
but the above x.cd.Subject1Mark isn't updated by x.ld.Subject1Mark although I use the answer in the linked stackoverflow question
The structure of LINQ query looks very similar to SQL:
var res =
from cur in CurrentData
join upd in DataToUpdate on upd.Id equals cur.Id
where (cur.Subject1Mark != upd.Subject1Mark || cur.Subject2Mark != upd.Subject2Mark || cur.Subject3Mark != upd.Subject3Mark)
select new {
Current = cur
, UpdatedSubject1Mark = upd.Subject1Mark
, UpdatedSubject2Mark = upd.Subject2Mark
, UpdatedSubject3Mark = upd.Subject3Mark
};
The main difference is that filtering out by inequality has moved from the on clause in SQL to a where clause of LINQ.

LINQ EF QUERY (view difference from condition & dynamic)

I need to make a query to filter records, when get distinct records, get these records information by difference conditions. Also I need these to be dynamic(quantity filter in first select)
Let me show you an example:
I have 2 tables:
tblCustomers:
id customerName
1 John
2 Philip
3 Steve
tblOrders
id customerId ordId payment
1 1 100 True
2 1 101 True
3 1 102 False
4 2 101 True
5 2 102 True
6 2 103 False
7 3 101 True
My condition is:
where (orderId = 101 and orderId = 102)
but get all records of this customer that payment = true I mean my condition is different from what I need to see.
I want to receive all records with payment=True without care of orderId
I must get:
john 100
john 101
Philip 101
Philip 102
Clearing: I need two step - first filter customer who has orderId=101&102, in second step i want to show these selected customers' orderId which payment is true. so for example in first step i get john(who has order id =101&102) then show john 100 - john 101 (which payment istrue). consider tblorder.id=1 isn't in first query but I must show in final result.
#Raphael direct me to better expression:I want to see all payment true order for the customers that have orders (101 & 102). but orderids may be more than 2 (thanks #Raphael).
2nd problem is: it must be dynamic. Sometimes I have more than 10 orderId that must be checked - sometimes less. I mean my query must be flexible.
In SQL Server select command, I can prepare a string variable and use but in linq I can't do it.
From what I understood from your post and the comments, you need all customers, where the orderId is 101 or 102 and the payment is true.
You need the where clause with the orderIds to be dynamic so you can change the Ids to be checked against outside of the query.
List<int> IDList = new List<int>();
IDList.Add(101);
IDList.Add(102);
IDList.Add(110);
//...
var result = from cust in tblCustomers
join order in tblOrders on cust.id equals order.customerId
where IDList.Contains(order.ordId) && order.payment == true
select new {
Name = cust.customerName
OrderId = order.ordId
payment = order.payment
//...
}
With this you can store all orderIds which need to be checked against in a list, which in turn you can edit from your code.
EDIT
I really haven't found a clean solution to your problem, so I took a detour, which isn't very clean but should work. In my example I created 2 classes, Customer & Order and filled it with your data from above. Then I took my first query and attached a groupBy to it and a where-clause comparing the length of the grouping with the length of the list
var result = (from cust in Customers
join order in Orders on cust.Id equals order.customerId
where IDList.Contains(order.orderId) &&
order.payment == true
select new {
Name = cust.Name,
OrderId = order.orderId,
Payment = order.payment
//...
}).GroupBy (r => r.Name)
.Where (r => r.Count() == IDList.Count());
Output:
Name OrderId Payment
Philip 101 True
Philip 102 True
If you want/need it, I can provide you with the whole Linqpad query, so you can see my whole code and what I have done. Speaking of Linqpad: ignore the result.Dump() line. It won't work on visual Studio.
void Main()
{
List<Customer> customers = new List<Customer>
{
new Customer { Id = 1, Name = "John" },
new Customer { Id = 2, Name = "Philip" },
new Customer { Id = 3, Name = "Steve" }
};
List<Order> orders = new List<Order>
{
new Order { Id = 1, CustomerId = 1, OrderId = 100, Payment = true },
new Order { Id = 2, CustomerId = 1, OrderId = 101, Payment = true },
new Order { Id = 3, CustomerId = 1, OrderId = 102, Payment = false },
new Order { Id = 4, CustomerId = 2, OrderId = 101, Payment = true },
new Order { Id = 5, CustomerId = 2, OrderId = 102, Payment = true },
new Order { Id = 6, CustomerId = 2, OrderId = 103, Payment = false },
new Order { Id = 7, CustomerId = 3, OrderId = 101, Payment = true }
};
List<int> orderIds = new List<int> { 101, 102 };
var customersWithRelevantOrders =
from ord in orders
group ord by ord.CustomerId into customerOrders
where orderIds.All (
i => customerOrders.Select (co => co.OrderId).Contains(i))
select customerOrders.Key;
var paymentTrueOrdersForTheseCustomers =
from ord in orders
join cust in customers on ord.CustomerId equals cust.Id
where ord.Payment
where customersWithRelevantOrders.Contains(cust.Id)
select new
{
Name = cust.Name,
OrderId = ord.OrderId
};
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
public bool Payment { get; set; }
}

How to perform "complex" join using Linq

I need to join two objects (tables) A and B. For any A there can be zero to many B's. The query needs the return one row per A.
The B's I want to order before the join to be able to select the needed row from B's following a certain condition. Say B has a column Type. If there is a Type 1 then that's the B I need, if not: Type 2 must be selected, etc.
Now I think about it, I am not sure how I would to this even in T-sql.
I think something like this:
SELECT A.*
FROM A LEFT JOIN (
SELECT * FROM B AS B1 WHERE B1.Type = (SELECT TOP 1 B2.Type FROM B AS B2
WHERE B2.JoinID = B1.JoinID
ORDER BY B2.Type )
) AS B ON B.JoinID = A.JoinID
[edit]
With the answer of sgtz I've tried to make it work. If have to make an additional step because the field I want to order by is not present. I add this field in step 1, in step 2 I make a selection of the keys and join everything in step 3, but there I receive an error "The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'GroupJoin'." on join "join a in adressen1 on new { b.TopRelatieID..."
var adressen1 = from a in db.Adres
select new
{
RelatieAdres = a,
Sortering = (int)(a.AdresType.Code == codeVestAdres ?
1 : a.AdresType.Code == codePostAdres ?
2 : (100 + (int)a.AdresType.Code.ToCharArray()[0]))
};
var adressen2 = from b in adressen1
group b by new { RelatieID = b.RelatieAdres.RelatieID } into p
let TopAdresType = p.Min(at => at.Sortering)
select new { TopRelatieID = p.Key.RelatieID, TopAdresType };
var q = from k in db.Klants
join b in adressen2 on k.RelatieID equals b.TopRelatieID into b_join
from b in b_join.DefaultIfEmpty()
join a in adressen1 on new { b.TopRelatieID, b.TopAdresType } equals new { a.RelatieAdres.RelatieID, a.Sortering } into a_join
from a in a_join.DefaultIfEmpty()
Here's a worked example. I did it two stages.
[Test]
public void Test333()
{
List<Order> O;
var M = Prepare333Data(out O);
var OTop = from o in O
group o by new {id=o.id, orderid=o.orderid}
into p
let topType = p.Min(tt => tt.type)
select new Order(p.Key.id, p.Key.orderid, topType);
var ljoin = from m in M
join t in OTop on m.id equals t.id into ts
from u in ts.DefaultIfEmpty()
select new {u.id, u.orderid, u.type};
}
public class Manufacturer
{
public Manufacturer(int id, string name)
{
this.id = id;
this.name = name;
}
public int id { get; set; }
public string name { get; set; }
}
public class Order
{
public Order(int id, int orderid, int type)
{
this.orderid = orderid;
this.id = id;
this.type = type;
}
public int orderid { get; set; }
public int id { get; set; }
public int type { get; set; }
}
private List<Manufacturer> Prepare333Data(out List<Order> O)
{
var M = new List<Manufacturer>() {new Manufacturer(1, "Abc"), new Manufacturer(2, "Def")};
O = new List<Order>()
{
new Order(1, 1, 2),
new Order(1, 2, 2),
new Order(1, 2, 3),
new Order(2, 3, 1)
,
new Order(2, 3, 1)
,
new Order(2, 3, 2)
};
return M;
}
response to comments:
your "new {" creates a new anonymous type. Two anonymous types created by difference processes are said to have the same signature if types are declared in the same order and they have the same type definition (i.e. int matches int, not int matches short). I haven't tested this scenario extensively in LINQ.
That's why I worked with real concrete classes, and not anon types within the JOIN portion. There's probably a way to rework it with pure LINQ, but I don't know what that is yet. I'll post you a response if it occurs to me okay.
I'd suggest using concrete classes too for now.
i.e. instead of
*new {*
when doing joins, always use
*new CLASSNAME(){prop1="abc",prop2="123"*
It's a little bit longer, but safer... safer at least until we work out what is going on inside the LINQ internals.
To be meaningful, you should add at least something to query result, not only A.*. Otherwise you'll have a copy of A with some rows possibly duplicated. If I understood the question correctly, this SQL query should work:
SELECT DISTINCT A.*, B.Type
FROM A LEFT JOIN
(SELECT TOP (1) JoinID, Type
FROM B
ORDER BY Type
GROUP BY JoinID, Type
) AS B ON A.JoinID = B.JoinID
Translated to LINQ, it is (UPDATED)
(from a in As
join b in
(from b1 in Bs
orderby b1.Type
group b1 by b1.JoinID into B1
from b11 in B1
group b11 by b11.Type into B11
from b111 in B11
select new { b111.JoinID, b111.Type }).Take(1)
on a.JoinID equals b.JoinID into a_b
from ab in a_b.DefaultIfEmpty()
select new { a_b.JoinID, /*all other a properties*/ a_b.Type }).Distinct()
LINQ may not work 100% correct, but you should grab the idea.

Resources