I am trying to change a join to LEFT outer join but getting all sorts of conversion errors. Below is my current join, can anybody provide any suggestion on how to do this without changing the actual logic of this join?
BRAND_NAME_MAP_MASTER objBrandNameMap = DB.PFC_MAP_MASTERs.Join(
DB.BRAND_NAME_MAPs,
a => a.BRAND_NAME_MAP_ID, b => b.BRAND_NAME_MAP_ID,
(a, b) => new { a, b }).Where(x => x.a.BRAND_NAME_MAP_ID == BrandNameMapID &&
x.b.BRAND_NAME_MAP_ID == BrandNameMapID).Select(x => x.a).FirstOrDefault();
Since you are only keeping a in the end changing to a left join implies not caring whether b matches or not, so the result is just:
BRAND_NAME_MAP_MASTER objBrandNameMap = DB.PFC_MAP_MASTERs
.Where(a => a.BRAND_NAME_MAP_ID == BrandNameMapID)
.FirstOrDefault();
You should use NetMage's answer if thats your entire query. But, if you still need to do a left outer join then use this:
BRAND_NAME_MAP_MASTER objBrandNameMap = DB.PFC_MAP_MASTERs.GroupJoin(
DB.BRAND_NAME_MAPs,
a => a.BRAND_NAME_MAP_ID,
b => b.BRAND_NAME_MAP_ID,
(a, b) => new { a, b })
.SelectMany(
x => x.b.DefaultIfEmpty(),
(x,y) => new { x.a, y})
.Where(x => x.a.BRAND_NAME_MAP_ID == BrandNameMapID)
.Select(x => x.a).FirstOrDefault();
Your intent doesn't make sense since you are only fetching a. If you only want a then there is no need of left join. Though i have written the query which will give you left join result. You can use it the way you want.
BRAND_NAME_MAP_MASTER objBrandNameMap =
(from Master in DB.PFC_MAP_MASTERs.Where(x=>x.BRAND_NAME_MAP_ID ==BrandNameMapID)
join Map in DB.BRAND_NAME_MAPs.Where(z=>z.BRAND_NAME_MAP_ID ==BrandNameMapID)
on Master.BRAND_NAME_MAP_ID equals Map.BRAND_NAME_MAP_ID
into result
from res in result.DefaultIfEmpty()
select new {Master,res}).ToLisT()
Hope it helps.
Related
Good morning,
I have the following query in EF Core which works as required if I do not add into the the where clause a condition for the createdtimestamp. If I add other conditions clauses to filter down the data as shown below the query will execute without issue.
var q = (
from trckhead in DbContext.TrackingBatchHeader
from userdets in DbContext.UserDetails
.Where(u => u.UserId == trckhead.ClosedByUserId).DefaultIfEmpty()
from trckkeys in DbContext.TrackingBatchesItemKey
.Where(t => t.TrackingNo == trckhead.TrackingNo).DefaultIfEmpty()
from trcklink1 in DbContext.TrackingBatchesLink
.Where(x => x.TrackingNo == trckhead.TrackingNo && x.TrackingType == "I").DefaultIfEmpty()
from trcklink2 in DbContext.TrackingBatchesLink
.Where(x => x.TrackingNo == trckhead.TrackingNo && x.TrackingType == "T").DefaultIfEmpty()
join trckref in DbContext.TrackingBatchesReference on trckhead.TrackingBatchType equals trckref
.TrackingBatchType
join mthsite in DbContext.SiteDetail on trckhead.SiteCode equals mthsite.SiteCode
join userdets2 in DbContext.UserDetails on trckhead.CreatedByUserId equals userdets2.UserId
where
(string.IsNullOrEmpty(searchRequest.Status) || trckhead.Status == searchRequest.Status) &&
trckhead.CreatedTimestamp >= DateTime.Now.AddMonths(-11)
select new TrackingBatchSearchResultDto
{
TrackingBatchId = trckhead.TrackingNo,
SiteCode = trckhead.SiteCode,
TrackingBatchType = trckhead.TrackingBatchType,
Status = trckhead.Status,
Created = trckhead.CreatedTimestamp,
CreatedById = trckhead.CreatedByUserId,
ClosedById = trckhead.ClosedByUserId,
UserDescription = trckhead.UsersDescription,
TrackingBatchDescription = trckref.TrackingBatchTypeDescription,
SiteName = mthsite.Title,
CreatedByName = userdets2.FullUserName ?? string.Empty,
ClosedByName = userdets.FullUserName ?? string.Empty,
Link1 = trcklink1.TrackingData ?? string.Empty,
Link2 = trcklink2.TrackingData ?? string.Empty
}).Distinct();
return await q.ToListAsync();
If I keep the date clause in the query the exception that is raised shows the following. I would have thought the server evaluation wouldnt be happy if I was passing a function into the query but checking against a simple date has thrown me. I could filter the results by date after obtaining the whole data set but the number of records returned can be massive so I would rather filter it down from a database query. Any help would be appreciated.
The LINQ expression 'DbSet<TrackingBatchHeader>
.SelectMany(
source: t => DbSet<UserDetail>
.Where(u => u.UserId == t.ClosedByUserId)
.DefaultIfEmpty(),
collectionSelector: (t, c) => new TransparentIdentifier<TrackingBatchHeader, UserDetail>(
Outer = t,
Inner = c
))
.SelectMany(
source: ti => DbSet<TrackingBatchesItemKey>
.Where(t0 => t0.TrackingNo == ti.Outer.TrackingNo)
.DefaultIfEmpty(),
collectionSelector: (ti, c) => new TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>(
Outer = ti,
Inner = c
))
.SelectMany(
source: ti0 => DbSet<TrackingBatchesLink>
.Where(t1 => t1.TrackingNo == ti0.Outer.Outer.TrackingNo && t1.TrackingType == "I")
.DefaultIfEmpty(),
collectionSelector: (ti0, c) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>(
Outer = ti0,
Inner = c
))
.SelectMany(
source: ti1 => DbSet<TrackingBatchesLink>
.Where(t2 => t2.TrackingNo == ti1.Outer.Outer.Outer.TrackingNo && t2.TrackingType == "T")
.DefaultIfEmpty(),
collectionSelector: (ti1, c) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>(
Outer = ti1,
Inner = c
))
.Join(
outer: DbSet<TrackingBatchesReference>,
inner: ti2 => ti2.Outer.Outer.Outer.Outer.TrackingBatchType,
outerKeySelector: t3 => t3.TrackingBatchType,
innerKeySelector: (ti2, t3) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>(
Outer = ti2,
Inner = t3
))
.Join(
outer: DbSet<SiteDetail>,
inner: ti3 => ti3.Outer.Outer.Outer.Outer.Outer.SiteCode,
outerKeySelector: s => s.SiteCode,
innerKeySelector: (ti3, s) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>, SiteDetail>(
Outer = ti3,
Inner = s
))
.Join(
outer: DbSet<UserDetail>,
inner: ti4 => ti4.Outer.Outer.Outer.Outer.Outer.Outer.CreatedByUserId,
outerKeySelector: u0 => u0.UserId,
innerKeySelector: (ti4, u0) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>, SiteDetail>, UserDetail>(
Outer = ti4,
Inner = u0
))
.Where(ti5 => ti5.Outer.Outer.Outer.Outer.Outer.Outer.Outer.CreatedTimestamp >= (Nullable<DateTime>)DateTime.Now.AddMonths(-11))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Plainly speaking, neglecting either establish navigation properties for the relationships between entities and instead using manual outer joins using "from" plus then adding the ultimate insult to poorly formed queries with Distinct is trying to use EF like a hammer to pound a square peg through a round hole.
With proper relationships, that query should be trivial.
Your datetime condition error message does not match your query.
** Edit: Removed details on Date comparison - This does not appear to be directly related as I did verify that the Nullable Date/DateTime comparisons do work with .AddMonth() in EF Core.
Expanding out conditional logic can help simplify the queries being executed.
For example:
var query = (/*build your base query*/);
if (!string.IsNullOrEmpty(searchRequest.Status))
query = query.Where(x => x.Status == searchRequest.Status);
if (searchRequest.DateFrom.HasValue)
{
var fromDate = searchRequest.DateFrom.Value.AddMonths(-11);
query = query.Where(x => x.CreatedTimestamp >= fromDate);
}
var results = query.Select(...).Distinct().ToList();
EF can perform some magic to build queries provided you give it enough information to do it properly. Seeing it build something like ti5.Outer.Outer.Outer.Outer.Outer.Outer.Outer.CreatedTimestamp should be sending off alarm bells. There may be something deep in your query lurking that is tripping a Client-side evaluation, or it is simply a case that a combination of joins and such has tipped over a complexity limit or exposed a bug in EF Core. The starting point would be to look to simplify that query expression by leveraging navigation properties rather than joins, and externalizing the conditional logic where you can.
If you have no recourse but to try and query across relationships that you are not prepared to map out properly with navigation properties, I would recommend exploring building your query in SQL as a Stored Procedure and then defining an Entity that can map to the resulting record from that Sproc.
I want to write left join using linq lambda expression. I have tried query using join but now I want to create using left join so any one can help me how can do.
Here this is my query:
var UserList = db.UserInfo
.Join(db.Course, u => u.id, c => c.userid, (u, c) =>
new { u, c }).GroupBy(r => r.u.id)
.Select(g => g.OrderByDescending(r => r.c.datetime)
.FirstOrDefault()).OrderByDescending(a => a.u.datetime).ToList();
Using this query, I don't want user data those who are not in course table, so I want to this data also in course table in userid in or not.
you can use
var qry = Foo.GroupJoin(
Bar,
foo => foo.Foo_Id,
bar => bar.Foo_Id,
(x,y) => new { Foo = x, Bars = y })
.SelectMany(
x => x.Bars.DefaultIfEmpty(),
(x,y) => new { Foo=x.Foo, Bar=y});
ref: How do you perform a left outer join using linq extension methods
I want to pick up all Sellers that aren't boss of a department.
How can I make this? In this query below, only the Sellers that are bosses of a department are picked up, I want the opposite of thereof.
My query:
var query = db.Sellers
.Join(db.Departments,
s => s.Id,
d => d.BossId,
(s, d) => new { Seller = s, Department = d })
.Where(a => a.Seller.Id == a.Department.BossId) ????
.Select(x => x.Seller).ToList();
In the "Where" part, I tried a => a.Seller.Id != a.Department.BossId, but it's wrong I have 3 sellers that aren't bosses.
I tried with this way too:
var listNonBoss = (from s in db.Sellers
join d in db.Departments on s.Id equals d.BossId
select s.Id).ToList();
I want just the opposite of these queries.
Sometimes it's easier to break it into multiple steps.
First, get the collection of all boss IDs:
var bossIDs = db.Departments.Select(x => x.BossId);
Then get all sellers whose IDs are not in that collection:
var listNonBoss = db.Sellers.Where(x => !bossIDs.Contains(x.Id)).ToList();
Join in your code will do an inner join, meaning it'll filter out sellers who don't have a boss.
To do the opposite you can do an outer join, and then remove the ones who have a boss. In fluent LINQ an outer join is done by doing a GroupJoin and then SelectMany.
Something like this:
var query = db.Sellers
.GroupJoin(db.Departments, s => s.Id, d => d.BossId, (s, d) => new { Seller = s, Department = d })
.SelectMany(x => x.d.DefaultIfEmpty(), (seller, department) => new { s.seller, department})
.Where(a => a.department.BossId == null)
.Select(x => x.Seller).ToList();
Or, using query syntax:
var listNonBoss = (from s in db.Sellers
join d in db.Departments on s.Id equals d.BossId into joinedTable
from jt in joinedTable.DefaultIfEmpty()
where jt.BossId == null
select s.Id).ToList();
This query takes a group of comments, then counts their upvotes and downvotes in the tblCommentVotes table.
At the moment, it counts these via the select new statement, in the form of a subquery. Would this be more efficient if it was in some sort of group by in the main query? Also if it would, could anyone show me how to do this, as I can't work out how you would do this.
// Get comments
var q = (
from C in db.tblComments
where
C.CategoryID == Category &&
C.IdentifierID == Identifier
join A in db.tblForumAuthors on C.UserID equals A.Author_ID
orderby C.PostDate descending
select new
{
C,
A.Username,
UpVotes = (from V in db.tblCommentVotes where V.CommentID == C.ID && V.UpVote == true select new { V.ID }).Count(),
DownVotes = (from V in db.tblCommentVotes where V.CommentID == C.ID && V.UpVote == false select new { V.ID }).Count()
}
)
.Skip(ToSkip > 0 ? ToSkip : 0)
.Take(ToTake > 0 ? ToTake : int.MaxValue);
What you need to do is to do an left outer join of the db.tblCommentVotes in the query expression, cause probably there might be no commentVotes?
When you have that, you should be able to perform ONE query in order to get your result.
It might look like this:
var q = (
from C in db.tblComments
where
C.CategoryID == Category &&
C.IdentifierID == Identifier
join A in db.tblForumAuthors on C.UserID equals A.Author_ID
// the following two lines are the left outer join thing.
join voteTemp in db.tblCommentVotes on voteTemp.CommentID equals C.ID into voteJoin
from vote in voteJoin.DefaultIfEmpty()
orderby C.PostDate descending
group C by new { Comment = C, Username = A.Username } into g
select new
{
g.Key.Comment,
g.Key.Username,
UpVotes = g.Count(x => x.UpVote),
DownVotes = g.Count(x => !x.UpVote)
}
)
.Skip(ToSkip > 0 ? ToSkip : 0)
.Take(ToTake > 0 ? ToTake : int.MaxValue);
This is untested and might not even compile, but I think it should be something like this.
db.tblComments.Where(c => c.CategoryID == Category && c.IdentifierID == Identifier)
.Join(db.tblForumAuthors, c => c.UserID, a => a.Author_ID,
(c, a) =>
new
{
CommentID = c,
AuthorName = a.UserName,
UpVotes = c.Join(db.tblCommentVotes, c => c.CommentID
v => v.CommentID,
(c, v) => v).Count(v => v.UpVote)
DownVotes = c.Join(db.tblCommentVotes, c => c.CommentID
v => v.CommentID,
(c, v) => v).Count(v => v.DownVote)
});
To optimise it's best first to measure.
Try, using something like LinqPad to view the generated SQL
Then use SQL Server Management Studio to see the query plan for that SQL
or:
Try running the code and seeing what SQL trace tells you is happening
Without the DB, it's quite hard (but fun) to guess whether that Linq will result in a single query or in multiple queries for working out the UpVotes and DownVotes. My guess is that calculating the UpVotes and DownVotes this way could be quite expensive - it may result in 2 additional queries per comment.
http://www.thereforesystems.com/view-query-generate-by-linq-to-sql/
without analyzing whats being output this question is impossible to answer.. however the link provided above should give you the tools necessary to perform this analysis yourself.
Does anyone know how to write this
var q = from c in customers
join o in orders on c.Key equals o.Key
select new {c.Name, o.OrderNumber};
In this syntax style?
var 1= customers.
.join(???)
.select(???)
I've been googling for a way to do this for days now with now luck. Everyone preferes the first syntax for tutorials, but I find the second much easier to determine order of operation when reading.
The compiler translation process involves using a "transparent identifier" that makes the current customer and order available to the Select method. You can emulate this by making your own:
customers.Join(orders, c => c.Key, o => o.Key, (c, o) => new { c, o })
.Select(x => new { x.c.Name, x.o.OrderNumber });
Instead, you could just move your Name/OrderNumber projection up into the Join call:
customers.Join(orders, c => c.Key, o => o.Key, (c, o) => new { c.Name, o.OrderNumber });
This just takes a single call to Enumerable.Join:
var q = customers.Join(
orders,
c => c.Key,
o => o.Key,
(c, o) => new {c.Name, o.OrderNumber}
);
You might also check out LinqPad . It has a small lambda button for the bottom half of the screen, and it translates the linq query to chain methods :
from p in PropertyListings
from rl in p.ResidentialListings
select new {p.YearBuilt,p.ListingUrl}
got translated to :
PropertyListings
.SelectMany (
p => p.ResidentialListings,
(p, rl) =>
new
{
YearBuilt = p.YearBuilt,
ListingUrl = p.ListingUrl
}
)