Translate complex query to linq - linq

I am trying to use the following query with Entity framework, that is why I am trying to use linq.
SELECT DISTINCT
elt.Type,
( SELECT TOP 1 Value
from ELD
where ELDTID = 2 AND ELID = el.ELID
) ID,
( SELECT TOP 1 Value
from ELD
Where ELDTID = 1 AND ELID = el.ELID
) Company,
( SELECT TOP 1 Value
from ELD
Where ELDTID = 5 AND ELID = el.EventLogID
) Message,
( SELECT Max(ET)
FROM EL el
INNER JOIN ELD eld ON eld.ELID = el.ELTID
WHERE el.ELID = el.ELID
) ET
FROM EL el
INNER JOIN ITS.ELT elt ON elt.ELTID = el.ELTID
WHERE ELSID = 3

It's not visible from the example whether ELSID is a EL or ELT attribute, assuming ELT.
I think to have spotted a mistake in your ET subquery: where el.ELID = el.ELID. You probably want to reference the el from the outer query on one side, but can't because you're using the same name. Used el1 in this answer for the subquery EL.
Tried to create a matching data model (next time please provide us with one, as you'll probably have it already).
public class Elt
{
public int ELTID;
public int ELSID;
public int Type;
}
public class El
{
public int ELID;
public int ELTID;
public int EventLogID;
}
public class Eld
{
public int ELDTID;
public int ELID;
public int ET;
public int Value;
}
public class Db
{
public IQueryable<El> Els;
public IQueryable<Elt> Elts;
public IQueryable<Eld> Elds;
}
Your SQL then should correspond to this query:
from el in db.Els
join elt in db.Elts on el.ELTID equals elt.ELTID
where elt.ELSID == 3
select new
{
elt.Type,
ID = (from eld in db.Elds where eld.ELDTID == 2 && eld.ELID == el.ELID select eld.Value).FirstOrDefault(),
Company = (from eld in db.Elds where eld.ELDTID == 1 && eld.ELID == el.ELID select eld.Value).FirstOrDefault(),
Message = (from eld in db.Elds where eld.ELDTID == 5 && eld.ELID == el.EventLogID select eld.Value).FirstOrDefault(),
ET = (
from el1 in db.Els
join eld in db.Elds on el1.ELTID equals eld.ELDTID
where el1.ELID == el.ELID
select eld)
.Max(eld => eld.ET),
};
If you don't have navigation properties (child collection of ELs on ELT, child collection of ELDs on EL, parent properties on EL and ELD) I'd suggest you add them, which would simplify the query further (join conditions injected by EF):
from elt in db.Elts
from el in elt.Els
...

Try this, may be you need
(from el in _context.EL.Where(p=>p.ELSID == 3)
join elt from _context.ELT on elt.ELTID equals el.ELTID
let ID = el.ELD.Where(p=>p.ELDTID == 2 ).Select(p=>p.Value).FirstOrDefault()
let Company = el.ELD.Where(p=>p.ELDTID == 1 ).Select(p=>p.Value).FirstOrDefault()
let Message = el.ELD.Where(p=>p.ELDTID == 5 && p.ELID = el.EventLogID).Select(p=>p.Value).Max()
let ET = el.Select(p=>p.ET).MAX()
select new
{
elt.Type,
ID,
Company,
Message,
ET
}).ToList();

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
});

Strongly typed linq group by

Relevant Model;
public class category_trans
{
[Key]
[Column(Order = 1)]
public int category_id { get; set; }
[Key]
[Column(Order = 2)]
public int language_id { get; set; }
public string name { get; set; }
}
present Linq query, working:
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
select ct;
I would like to group query result in order to get distinct category_trans.name (now I am getting multiple ones).
Trying
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
group ct by ct.name into g
select new
{
category_id = g.category_id,
name = g.name
};
gives me errors on both g.category_id and g.name
IGrouping <string,category_trans> does not contain a definition for 'category_id'...
Why grouping seems to lose reference to model members and how may it be fixed?
Because a group can contain multiple, you can use the Key property for the name:
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
group ct by ct.name into g
select new
{
category_id = g.First().category_id,
name = g.Key
};
I have used First to get the first category_id, you might want to use a different logic.

convert this SQL to link Left Outer Join and a Case statement

I am having problem converting this sql statement into Linq. It has a Left outer join and a case statement. I will try the answer given below.
SELECT [job_required_document].[job_required_document_id]
,[document_name]
,[document_description]
,CASE WHEN dlink.job_required_document_id is NULL THEN 0 ELSE 1 END AS DocIsRequired
FROM [dbo].[job_required_document]
LEFT OUTER JOIN [dbo].[job_description_document_link] dlink ON job_required_document.job_required_document_id = dlink.job_required_document_id
AND dlink.job_description_id = #JobDescriptionId
**This is what i had done. But the docIsRequired is coming 1 for all the records. I think the case statement is wrong.
var query = from document in context.job_required_documents
join dlink in context.job_description_document_links.Where(q => q.job_description_id == job_description_id && q.job_description_id == job_description_id)
on document.job_required_document_id equals dlink.job_required_document_id into documentdlink
from q in documentdlink.DefaultIfEmpty()
select new Documents
{
job_description_id = job_description_id,
job_required_document_id = document.job_required_document_id,
document_name = document.document_name,
document_description = document.document_description,
docIsRequired = document.job_required_document_id == null ? 0 : 1
};
Create a custom class with following details:
public class DocumentDetails
{
public int docID {get;set;}
public string docName {get;set;}
public string docDesc {get;set;}
public int docIsRequired {get;set;}
}
Now use this LINQ to fetch data into this class type's collection as mentioned below:
List<DocumentDetails> documentDetialsList = (from document in datacontext.job_required_document
from dlink in datacontext.job_description_document_link
.where(x=> x.job_required_document_id == document.job_required_document_id).DefaultIfEmpty()
where dlink.job_description_id == #JobDescriptionId
select new {
docID = document.[job_required_document_id],
docName = document.[document_name],
docDesc = document.[document_description],
docIsRequired = dlink.job_required_document_id == null ? 0 : 1
}).ToList();
Hopefully it would help...

Linq: Using Distinct on calculated result

I have a query which calculates a range of dates which I want to run a distinct query on to return just Year/Month combinations. I can't work out how best to achieve this.
private IEnumerable<DateTime> MonthsWithItineraryData (int Id) // shipId
{
var months = (from i in context.Itineraries
join isd in context.ItineraryStartDates on i.Id equals isd.ItineraryId
join id in context.ItineraryDay on i.Id equals id.ItineraryId
where i.ShipId == Id
select (DateTime)DbFunctions.AddDays(isd.Date, id.Day)
);
return (months);
}
I think you want something like this:
return monts.GroupBy(x => new {x.Month, x.Year})
.Select(x => x.First());
or else you can a custom equality comparer as distinct argument:
public class DateTimeComparer : IEqualityComparer<DateTime>
{
public bool Equals(DateTime x, DateTime y)
{
return x.Year == y.Year && x.Month == y.Month;
}
public int GetHashCode(DateTime obj)
{
return obj.Year * 100 + obj.Month;
}
}
result = months.Distinct(new DateTimeComparer()).ToList();

The type of one of the expressions in the join clause is incorrect. Types inference failed in the call to 'Group Join'

var val1 =
(from main in
(from irac in objIRAC
join iip in objIIP on irac.InvoiceItemID equals iip.InvoiceItemID
join oritc in
(from ritc in objRITC
join iips in objIIP on ritc.InvoiceItemPremiumID equals iips.InvoiceItemPremiumID
where ritc.ReconID == ReconId
&& ritc.BookOfBusinessInvoiceItemPremiumID == BookOfBusinessInvoiceItemPremiumID
&& ritc.InvoiceID == InvoiceId
group new { ritc, iips } by new { ritc.ReconID, iips.InvoiceItemID }
into ritcs
select new
{
ritcs.Key.ReconID,
ritcs.Key.InvoiceItemID
}) on ReconId equals oritc.ReconID into oritcs // ReconId
from oritc in oritcs.DefaultIfEmpty()
where iip.InvoiceItemPremiumID == BookOfBusinessInvoiceItemPremiumID
select new
{
oritc.InvoiceItemID,
irac.CommissionOnTypeID,
irac.BOBFamilyID,
irac.RateTypeID,
irac.Rate,
irac.CommissionAdjustmentFlag
})
join sub in objIRAC // Here getting the error
on new { main.InvoiceItemID, main.CommissionOnTypeID, main.BOBFamilyID, main.RateTypeID, main.Rate, main.CommissionAdjustmentFlag }
equals new { sub.InvoiceItemID, sub.CommissionOnTypeID, sub.BOBFamilyID, sub.RateTypeID, sub.Rate, sub.CommissionAdjustmentFlag }
into subs
from osub in subs.DefaultIfEmpty()
select new { osub.InvoiceItemID });
Check the types of
main.InvoiceItemID, main.CommissionOnTypeID, main.BOBFamilyID, main.RateTypeID, main.Rate, main.CommissionAdjustmentFlag
and
sub.InvoiceItemID, sub.CommissionOnTypeID, sub.BOBFamilyID, sub.RateTypeID, sub.Rate, sub.CommissionAdjustmentFlag
They should match exactly. That is: if one property is a Nullable<int> and the corresponding property is int it does not match. Since all properties but InvoiceItemID are from the same entity InvoiceItemID is probably the culprit.
Odds are one of your foreign key references are nullable, you'll need to do an explicit cast in order for the join (presuming inner join) to work.
var result =
from a in context.T1
join b in context.T2
on new { s.Field1, s.Field2 }
equals new {
Field1 = (int)b.T3.Field1 /* Join via sub-table, explicit conversion from int? to int */
,Field2 = (int)b.Field2 /* explicit conversion from int? to int */
}
where
b.T3.Field1 != null /* Specify no nulls allowed for inner-join fields */
&& b.Field2 != null
&& b.Field3 == InputVar1 /* Input Search Variables */
&& b.Field4 == InputVar2
select a;
The above example was renamed from an actual query I am using.

Resources