Outer join with Linq causing "GroupJoin" error - linq

The following join projection is throwing the error, "The 'GroupJoin' operation must be followed by a 'SelectMany' operation where the collection selector is invoking the 'DefaultIfEmpty' method." I've run over a few permutations of changes, but haven't been able to figure it out. Thoughts?
EDIT: It's looking more and more like this may be a Dynamics CRM issue. The Xrm models I'm accessing for data have been generated by the CRM SDK (CRM 2011).
var q =
left
.GroupJoin(right,
c => c.Id,
cl => cl.c.Id,
(c, cs) => new { c, cs })
.Where(x=>x.c.Name.Contains("some text"))
.SelectMany(x => x.cs.DefaultIfEmpty(), (x, csubl) =>
new
{
CompanyName = x.c.Name
});

i think in query syntax it will be more beautiful
var q = from l in left
join r in right on l.Id equals r.c.Id into groupped
from g in groupped.DefaultIfEmpty()
where l.Name.Contains("some text")
select new {
CompanyName = l.Name;
}
UPDATE
in samples from msdn have sample with left join, so you can try my code above, or move Where before GroupJoin or after SelectMany

Related

How to write SQL translateable linq code that groups by one property and returns distinct list

I want to change code below to be sql translateable because now i get exception.
Basicallly i want list of customers from certain localisation and there could be more than one customer with the same CustomerNumber so i want to take the one that was most recently added.
In other words - distinct list of customers from localisation where "distinct algorithm" works by taking the most recently added customer if there is conflict.
The code below works only if it is client side. I could move Group By and Select after ToListAsync but i want to avoid taking unnecessary data from database (there is include which includes list that is pretty big for every customer).
var someData = await DbContext.Set<Customer>()
.Where(o => o.Metadata.Localisation == localisation)
.Include(nameof(Customer.SomeLongList))
.GroupBy(x => x.CustomerNumber)
.Select(gr => gr.OrderByDescending(x => x.Metadata.DateAdded).FirstOrDefault())
.ToListAsync();
Short answer:
No way. GroupBy has limitation: after grouping only Key and Aggregation result can be selected. And you are trying to select SomeLongList and full entity Customer.
Best answer:
It can be done by the SQL and ROW_NUMBER Window function but without SomeLongList
Workaround:
It is because it is not effective
var groupingQuery =
from c in DbContext.Set<Customer>()
group c by new { c.CustomerNumber } into g
select new
{
g.Key.CustomerNumber,
DateAdded = g.Max(x => x.DateAdded)
};
var query =
from c in DbContext.Set<Customer>().Include(x => x.SomeLongList)
join g in groupingQuery on new { c.CustomerNumber, c.DateAdded } equals
new { g.CustomerNumber, g.DateAdded }
select c;
var result = await query.ToListAsync();

Linq Query join on multiple conditions

Trying to do a single query which combined data from multiple joins into a single property
Rig
----
RigId
Component1Id
Component2Id
Component3Id
Work Item
---------
Id
ComponentID
Description
I'm trying to do a query that returns a list of rigs with a single property called history that consists of all the workItems associated with the components in a Rig.
I cant seem to do multiple conditions in a join or do separate joins and concatenate the items into a single property.
So the result is something like
RigId, History (which consists of a list of all the workitems for the rig)
Here is the answer in query syntax:
var ans = from r in Rigs
join w in WorkItems on r.Component1ID equals w.ComponentID into wg1
join w in WorkItems on r.Component2ID equals w.ComponentID into wg2
join w in WorkItems on r.Component3ID equals w.ComponentID into wg3
select new { r.RigID, History = wg1.Concat(wg2).Concat(wg3).ToList() };
and if you prefer, lambda syntax (this was a bit harder...)
var ans2 = Rigs.GroupJoin(WorkItems, r => r.Component1ID, w => w.ComponentID, (r, w1g) => new { r, h1 = w1g.ToList() })
.GroupJoin(WorkItems, rh1 => rh1.r.Component2ID, w => w.ComponentID, (rh1, w2g) => new { rh1.r, h2 = rh1.h1.Concat(w2g.ToList()) })
.GroupJoin(WorkItems, rh2 => rh2.r.Component3ID, w => w.ComponentID, (rh2, w3g) => new { rh2.r.RigID, History = rh2.h2.Concat(w3g.ToList()) });
I don't think using columns for the components if a very good idea - what happens when a Rig has more or fewer than 3 components? You should really have a separate RigComponent table.

How to perform LINQ left outer join using method syntax?

Microsoft has this help page for performing a left outer join, however it's in linq query syntax. What's the equivalent to this, using method syntax?
http://msdn.microsoft.com/en-us/library/bb397895.aspx
For example, I have two enumerables:
class TA {string Name{get;}}
class TB {string Name{get;}}
Enumerable<TA> A;
Enumerable<TB> B;
The result I want is this:
var joined =
A.Select(a => new
{ left = a,
right = B.FirstOrDefault(b => b.Name == a.Name)
});
This gives me what I need with just select and (effectively) a nested select. Perhaps this isn't an actual left outer join...
I've used something like
var q = (from a in db.Item1Set
from b in db.Item2Set.Where(i2 => i2.Item1Id == a.Id).DefaultIfEmpty());

Get data from multiple tables to an object with LINQ join

I am trying to get from 3 related tables by using LINQ. But when I use 2 joins, the result takes only elements getting from 2nd join. Here is my code:
var myAssList = mldb.Assigns
.Join(mldb.Lists,
a => a.list_id,
l => l.id,
(a, l) => new {
Assign = a,
List = l
})
.Where(a => a.Assign.assigned_to == "myname")
.Join(mldb.Elements,
li => li.List.id,
e => e.parent_server_id,
(li, e) => new {
Element = e
});
var jsonSerialiser = new JavaScriptSerializer();
var listListJson = jsonSerialiser.Serialize(myAssList);
this Json return only attributes from Element(e) and List(li). But I want to get also the attributes from Assign(a).
The SQL query I am trying to realize in LINQ is that:
select * from Assigns
inner join Lists
on Assigns.server_list_id=Lists.id
inner join Elements
on Lists.id=Elements.parent_id
where Assigns.assigned_to='myname'
So, how can I get the attributes from the first join also (from "a", "l" and "e")?
You can access Assign entity from outer sequence li variable:
.Join(mldb.Elements,
li => li.List.id,
e => e.parent_server_id,
(li, e) => new {
Element = e,
Assign = li.Assign // here
});
from a in mldb.Assigns
join l in mldb.Lists on a.list_id equals l.id
join e in mldb.Elements on l.id equals e.parent_server_id
where a => a.Assign.assigned_to == "myname"
select new { Assign = a, Element = e }
This is so called "query syntax". It makes LINQ expressions looks like SQL queries.
In the end they are translated to IEnumerable extension methods. If you want
to join multiple tables then query syntax is more readable. Another useful feature
of query syntax is let clause. With the aid of it, you can declare additional variables
inside your queries.

nHibernate 3 - Left Join re-Linq solution

I am trying to to run this Linq query below with nHibernate 3.
var items = from c in session.Query<tbla>()
join t in session.Query<tblb>() on c.Id equals t.SomeId into t1 // use left join on trades.
from t2 in t1.DefaultIfEmpty()
select new {item = c, desc = t2.Description};
This is the stock way to perform a left join in linq to my knowledge. However it's giving me an unsupported exception message. How can I achieve a basic left join without resorting back to HQL? This seems somewhat silly that an ORM as prevalent as nHibernate cannot support something as pedestrian as a left join.
[edit]
I've put the real answer to my own question below.
After further research on this; this is possible (although not obvious) to achieve in a strongly typed fashion using QueryOver. The trick is to use outer Query alias variables in conjunction with WithAlias, and TransformUsing. Here is an example that does left join with filtering and sorting.
// Query alias variables
entityTypeA anchorType = null;
entityTypeB joinedType = null;
var items = session.Query<entityTypeA>( ()=>anchorType )
.Left.JoinAlias(() => anchorType.FieldName, () => joinedType)
.WithSubquery.WhereProperty(e => e.FieldD).In(myFilterList)
// bind property mappings using WithAlias
.SelectList(list => list
.Select(e => e.FieldNameA).WithAlias( ()=> anchorType.FieldNameA )
.Select(e => e.FieldNameB).WithAlias( ()=> anchorType.FieldNameB )
)
.OrderBy(e => joinedType.FieldNameC).Desc
.TransformUsing(Transformers.AliasToBean<entityTypeA>()) // transform result to desired type.
.List<entityTypeA>();
It's not supported yet. HQL is your only choice at the moment.

Resources