JOIN and LEFT JOIN equivalent in LINQ with Method Syntax - sql

I am converting a SQL query to LINQ that creates a left join with 1-to-1 mapping, and it has to be in Method Syntax. I have been pulling off my hair trying to accomplish this to no veil. I can do it in Lambda Syntax. Below is the example query I am trying to run. They are not actual code. Would someone point out what I am doing wrong?
SQL:
SELECT item.*, item_status.*
FROM item
LEFT JOIN item_status
ON item.ID = item_status.itemID
AND item_status.FLAGGED = true
WHERE item.published_date > "2008-06-19"
LINQ:
var linq_query = (
from selected_item in item
join selected_item_status in item_status
on selected_item.ID equals item_status.itemID into joined
from item_status in joined.DefaultIfEmpty()
where item_status.FLAGGED = true
select new {selected_item, selected_item_status}).ToList();

The join ... into becomes a GroupJoin and the second from becomes a SelectMany:
var linq_query = Item
.GroupJoin(
item_status.Where(x => x.selected_item_status.FLAGGED), // EDIT: Where clause moved here.
selected_item => selected_item.ID,
selected_item_status => selected_item_status.itemID,
(selected_item, joined) => new
{
selected_item,
statuses = joined.DefaultWithEmpty(),
})
.SelectMany(x => x.statuses.Select(selected_item_status => new
{
x.selected_item,
selected_item_status,
}))
// EDIT: Removed where clause.
.ToList();
It looks like the Where makes the left outer join unnecessary, as null statuses will be filtered out anyway.
EDIT: No, upon reviewing the SQL it looks like your LINQ query is slightly incorrect. It should be:
var linq_query = (
from selected_item in item
join selected_item_status
in (
from status in item_status
where status.FLAGGED
select status)
on selected_item.ID equals item_status.itemID into joined
from item_status in joined.DefaultIfEmpty()
select new {selected_item, selected_item_status}).ToList();

Related

Convert sql query to EF

Can anybody please help with this SQL query to transform it to EF Linq Expression?
select MI.Id, B.Count, B.Cost, Name, Price, Unity, I.Count, I.Cost, BOL.Date, BOL.type, BOL.Number
from Balance B inner join MaterialsInfo MI on Mi.Id = B.MaterialsId
inner join Income I on MI.Id = I.MaterialsId
inner join BillOfLading BOL on I.BillOfLadingId = BOL.Id
where B.Id in (select Id from (select max(Date), Id from Balance group by Balance.MaterialsId))
and I.Id in (select Id from(select max(Date), Income.Id as Id from Income inner join BillOfLading BOL on Income.BillOfLadingId = BOL.Id group by Income.MaterialsId))
I've been trying to do this for days, so I could really use some help.
Do you need extra info?
Thank you in advance.
Edit
Here is a little part of the diagram, maybe it helps
It seems the subqueries in both of your conditions in where clause is incorrect. Because your
group by column is missing in the select statement
And it is very unclear what you trying to achieve from the subqueries.
Without a clear understanding of your subqueries, all I can prepare for you is this.
var result = await (from balance in yourDBContect.Balances
join material in yourDBContect.MaterialsInfos on balance.MaterialsId equals material.Id
join income in yourDBContect.Incomes on material.Id equals income.MaterialsId
join bill in yourDBContect.BillOfLadings on income.BillOfLadingId equals bill.Id
where Balances.GroupBy(g => g.MaterialsId)
.Select(s => new
{
MaterialsId = s.Key,
MaxDate = s.Max(x => x.Date)
}).ToList().Contains(new { balance.MaterialsId, MaxDate = balance.Date })
&& Incomes.Join(BillOfLadings,
p => p.BillOfLadingId,
e => e.Id,
(p, e) => new { p, e })
.GroupBy(g => g.p.MaterialsId)
.Select(s => new
{
MaterialsId = s.Key,
MaxDate = s.Max(s => s.e.Date)
}).ToList().Contains(new { income.MaterialsId, MaxDate = balance.Date })
select new
{
material.Id,
balance.Count,
balance.Cost,
balance.Name,
balance.Price,
balance.Unity,
ICount = income.Count,
ICost = income.Cost,
bill.Date,
bill.Type,
bill.Number
});
Notes:
Alias for Income.Count and Income.Cost are changed because an object cannot contain exactly same nenter code hereamed property.
Linq for a proper subquery will replace new List<int>().

Linq to SQL include related table

Been trying for a while now, but just can't get it to work the way I want. Referring to the code snippet below, I want to include a related table to Bricks (BrickColors). As of now BrickColors are not included and is lazy loaded.
var query = (from ul in DbContext.UserLocs
join l in DbContext.Locs on ul.LocId equals l.Id
join lb in DbContext.LocBricks on l.Id equals lb.LocId
join b in DbContext.Bricks on lb.BrickId equals b.Id
join bc in DbContext.BrickColors on b.ColorId equals bc.Id
where ul.UserId == userId
group new { LocQty = ul.Quantity, LocBrickQty = lb.Quantity, Brick = b } by new { b.BrickId, b.ColorId }
into data
orderby data.Key
select new
{
Brick = data.FirstOrDefault().Brick,
Quantity = data.Sum(d => d.LocBrickQty * d.LocQty)
})
.AsNoTracking();
If I remove .AsNoTracking() the performance is quite good, because it keeps the BrickColors table in memory, but I want it to be included in the query from the start.
I have tried DbContext.Bricks.Include(b => b.BrickColorAccessor) in the query, but that doesn't work. I think my group new { } is messing something up since I don't include BrickColors there...

Making thing more succint using linq to entities for inner joins

Any way to make this less verbose?
var model =
(
from MvrTable in
LinqEntitiesCtx.Mvrs
join MvrMedsTable in LinqEntitiesCtx.MvrMeds
.Where(Id => Id.FKMvrId == 1)//inner join will be fast with this!
on MvrTable.PKMvrId equals MvrMedsTable.FKMvrId
join MvrLocationTable in LinqEntitiesCtx.MvrLocations
on MvrTable.PKMvrId equals MvrLocationTable.FKMvrId
join MvrEmployeeTable in LinqEntitiesCtx.MvrEmployees
on MvrTable.PKMvrId equals MvrEmployeeTable.FKMvrId
//notice i am using a different primary key that previouslly
join MvrMedsAdminRouteTable in LinqEntitiesCtx.MvrMedsAdminRoutes
on MvrMedsTable.PKMvrMedsId equals MvrMedsAdminRouteTable.FKMvrMedsId
select new
{ //here I choose the columns I want to display
MvrTable.PKMvrId,
MvrTable.VarianceDescription,
MvrTable.CaseNumber,
MvrTable.DateOfReport,
MvrTable.DateOfVariance
}
);
Equivalent SQL code of above:
SELECT [t0].[PKMvrId], [t0].[VarianceDescription], [t0].[CaseNumber], [t0].[DateOfReport], [t0].[DateOfVariance], [t1].[PKMvrMedsId]
FROM [Mvrs] AS [t0]
INNER JOIN [MvrMeds] AS [t1] ON ([t0].[PKMvrId]) = [t1].[FKMvrId]
INNER JOIN [MvrLocations] AS [t2] ON ([t0].[PKMvrId]) = [t2].[FKMvrId]
INNER JOIN [MvrEmployees] AS [t3] ON [t0].[PKMvrId] = [t3].[FKMvrId]
INNER JOIN [MvrMedsAdminRoutes] AS [t4] ON ([t1].[PKMvrMedsId]) = [t4].[FKMvrMedsId]
WHERE [t1].[FKMvrId] =ParamMvrId
By using Associations it could probably be written more compact. Something like (not complete):
var model = from MvrTable in LinqEntitiesCtx.Mvrs
where MvrTable.MvrMeds.MvrLocations.Any() //These are the Associations
select new
{
MvrTable.PKMvrId,
MvrTable.VarianceDescription,
MvrTable.CaseNumber,
MvrTable.DateOfReport,
MvrTable.DateOfVariance
};
You don'y really need the joins since you are not getting any data from those tables. You should use Any instead which corresponds to SQL's EXISTS.
I believe changing the join's to from's will make it more clear. You could also abbreviate your entity alias's
var model =
(
from MvrTable in LinqEntitiesCtx.Mvrs
from MvrMedsTable in LinqEntitiesCtx.MvrMeds
.Where(Id => Id.FKMvrId == 1)
.Where(x => MvrTable.PKMvrId == x.FKMvrId)
from MvrLocationTable in LinqEntitiesCtx.MvrLocations
.Where(x => MvrTable.PKMvrId == x.FKMvrId)
from MvrEmployeeTable in LinqEntitiesCtx.MvrEmployees
.Where(x => MvrTable.PKMvrId == x.FKMvrId)
from MvrMedsAdminRouteTable in LinqEntitiesCtx.MvrMedsAdminRoutes
.Where(x => MvrMedsTable.PKMvrMedsId == x.FKMvrMedsId)
select new
{
MvrTable.PKMvrId,
MvrTable.VarianceDescription,
MvrTable.CaseNumber,
MvrTable.DateOfReport,
MvrTable.DateOfVariance
}
);

how can I change a sql query to linq 2 nhibernate?

I have a query that is driving me crazy,,when I run it in sql it works fine but I dont know how to change it to linq to sql
the query is:
SELECT organizationstructure.PositionTitle.Title, organizationstructure.Person.FirstName, organizationstructure.Person.LastName,
organizationstructure.Department.Name
FROM organizationstructure.Department INNER JOIN
organizationstructure.Accountability AS Accountability_1 ON organizationstructure.Department.PartyId = Accountability_1.ParentPartyId INNER JOIN
organizationstructure.Accountability INNER JOIN
organizationstructure.Person ON organizationstructure.Accountability.ChildPartyId = organizationstructure.Person.PartyId INNER JOIN
organizationstructure.Position ON organizationstructure.Accountability.ParentPartyId = organizationstructure.Position.PartyId ON
Accountability_1.ChildPartyId = organizationstructure.Position.PartyId INNER JOIN
organizationstructure.PositionTitle ON organizationstructure.Position.PositionTitleId = organizationstructure.PositionTitle.PositionTitleId
and I think this is wrong but I changed it to:
query// query is iqueryable of position
.Join(Repository<Accountability>.Find(), p => p.Id, a => a.Child.Id,
(p, a) => new Tuple<string, string, int?>(((Department)a.Parent).Name, p.PositionTitle.Title, p.Id))
.Join(Repository<Accountability>.Find(), p => p.Item3, p => p.Parent.Id,
(p, d) => new Tuple<string, string, int?, string>(p.Item1, p.Item2, p.Item3, d.Child == null ? string.Empty : string.Format("{0}", ((Person)d.Child).FirstName) + " " + ((Person)d.Child).LastName))
whats wrong with it or ow can i change this query??
Generally having to do excessive numbers of explicit joins in your linq to nhibernate is a sign that you've not mapped your database to your domain appropriately. Without mappings between objects then you will just be reproducing SQL in your linq, which is a bit of a waste of time.
Your mappings should specify the relationships between the objects in your domain - for instance, a "Person" might have a reference to a "PositionTitle". If you use the mappings to setup relationships in this way then your query could end up looking something like this:
var results =
from
p in mySession.Query<Person>
select
new PersonalDetails
{
Title = p.PositionTitle.Title,
FirstName = p.FirstName,
LastName = p.LastName
DepartmentName = p.Party.Department.Name
};

Performing a left outer join when an nhibernate filter is applied

I am trying to perform a LEFT OUTER JOIN with nhibernate criteria. I also have a filter that gets applied to my queries.
The problem I have is the filter stops the left outer join working properly if the join result is null.
As a very simple example I want to return all the musicians, and if they are in a band then also their band
NHibernate generates the following sql
SELECT this_.Name, band2_.Name
FROM Musicians this_
left outer join [Band] band2_
on this_.BandID = band2_.ID
WHERE (band2_.IsDeleted = 0)
which won't return the musicians if they aren't in a band. What I want is something like
SELECT this_.Name, band2_.Name
FROM Musicians this_
left outer join [Band] band2_
on this_.BandID = band2_.ID
WHERE this_.ID = 4894 /* #p3 */
(band2_.ID IS NULL OR band2_.IsDeleted = 0)
Is this possible with nhibernate?
UPDATE
var projections = new[]
{
Projections.Property("Musician.Name").As("MusicianName"),
Projections.Property("Band.Name").As("BandName")
};
return this.sessionProvider.GetSession().CreateCriteria<Musician>("Musician")
.CreateCriteria("Musician.Band", "Band", JoinType.LeftOuterJoin)
.SetProjection(projections)
.Add(Restrictions.Eq("Musician.ID", parameters.MusicianId))
.SetResultTransformer(Transformers.AliasToBean<MusicianDetailsResult>())
.UniqueResult<MusicianDetailsResult>();
The filter is defined with FluentNHibernate
this.WithName(FilterName).WithCondition("IsDeleted = 0")
This is a bug in NHibernate.
I used proposed workaround and set useManyToOne on the filter to false. This property isn't currently in FluentNhibernate so I just do it in ExposeConfiguration
foreach (var key in cfg.FilterDefinitions.Keys)
{
filter = cfg.FilterDefinitions[key];
cfg.FilterDefinitions[key] = new FilterDefinition(
filter.FilterName,
filter.DefaultFilterCondition,
filter.ParameterTypes, false);
}
Firstly, this is much easier if you simply map Band to Musician as a reference:
public class MusicianDbMap : ClassMap<Musician>
{
public MusicianDbMap()
{
...
References(x => x.Band)
.Nullable()
.Not.LazyLoad(); // Or lazy load... either way
}
}
Then you can just run a simple query - here it is in Linq-2-NHibernate:
Session.Linq<Musician>()
.Where(x => x.Band == null || !x.Band.IsDeleted)
.ToList();
Secondly, I'm not sure about this statement of yours: "which won't return the musicians if they aren't in a band"... I'm not sure if that is correct. A left outer join should return all rows, regardless of whether they are in a band or not - are you sure that you haven't made an error somewhere else?