How i can obtain the similar Linq query as this SQL query using Linq method based?
SELECT * FROM F_ARTICLE A
LEFT JOIN F_ARTFOURNISS AF ON AF.AR_Ref = A.AR_Ref AND AF.AF_Principal = 1
ORDER BY A.AR_Design DESC
OFFSET 500 ROWS FETCH NEXT 100 ROWS ONLY
I'm using method based due to System.Linq.Dynamic requirements.
The mapping is like this:
I started by this but i don't know how to limit to AF_Principal = 1:
var query = context.F_ARTICLE
.Join(
context.F_ARTFOURNISS,
a => a.AR_Ref,
t => t.AR_Ref,
(a, t) => new { a, t })
.Select(
z => new ArticleItem()
{
Article = z.a,
Tarif = z.t.FirstOrDefault()
})
.OrderBy($"{field} {direction}");
return query.Skip(startIndex).Take(count).ToList();
The code is from my head but the point is using DefaultIfEmpty for doing LEFT JOIN:
var query = from farticle in context.F_ARTICLE
join b in context.F_ARTFOURNISS
on new {farticle.AR_Ref, AF_Principal = 1} equals new {b.AR_Ref, b.AF_Principal} into gj
from subres in gj.DefaultIfEmpty()
select farticle;
return query.Skip(startIndex).Take(count).ToList();
Also you can limit by AF_Principal using Where clause (I think DBMS engine will optimize the query anyway)
see if this works
var query = from F1 in context.F_ARTICLE join F2 in context.F_ARTFOURNISS
on F1.AR_Ref equals F2.AR_Ref into newF
from F3 in newF.where(f=>f.AF_Principal == 1).DefaultIfEmpty()
select f;
return query.Skip(startIndex).Take(count).ToList();
I think i found the query:
var query = context.F_ARTICLE
.GroupJoin(
context.F_ARTFOURNISS,
a => a.AR_Ref,
t => t.AR_Ref,
(a, t) => new { a, t })
.Select(
z => new ArticleItem()
{
Article = z.a,
Tarif = z.t.Where(t=>t.AF_Principal == 1).FirstOrDefault()
})
.OrderBy($"{field} {direction}");
return query.Skip(startIndex).Take(count).ToList();
Related
There seem to be lots of questions about SQL to LINQ, but I can't seem to find examples with joined tables and grouping; specifically with a need to get data from multiple tables.
Take this simple SQL:
SELECT
s.showId, s.showName, v.venueName, Min(dateTime) startDate
FROM
shows s
INNER JOIN venues v ON s.venueId = v.venueId
INNER JOIN showDates d ON s.showId = d.showId
GROUP BY
s.showId
The best I can come up with is the following
var ungrouped = (
from s in db.Shows
join v in db.Venues on s.VenueId equals v.VenueId
join d in db.ShowDates on s.ShowId equals d.ShowId
select new { s, v, d }
).ToList();
var grouped = (
from s in ungrouped
group s by s.s.ShowId into grp
select new
{
showId = grp.Key,
name = (from g in grp select g.s.showName).FirstOrDefault(),
venue = (from g in grp select g.v.VenueName).FirstOrDefault(),
startDate = grp.Max(g => g.d.DateTime)
}
);
This works but it feels messy. I don't like:
It being split into two statements
Having to repeatedly write (from g in grp select ...).FirstOrDefault()
Bits like s.s.ShowId
How its vastly more lines of code than the SQL
This example is a simple one, it only gets worse when I have 5+ tables to join and 10+ columns to select.
Question: Is this the best way to do this, and I should just accept it; or is there a better way to write this query?
I am not sure if you are looking for something like this but it's a bit cleaner, it's not split in 2 statements and you might find it helpful. I couldn't use a dbcontext so I used lists to make sure the syntax is correct.
var res = Shows.Join(Venues,
show => show.VenueID,
venue => venue.VenueID,
(show, venue) => new { show, venue })
.Join(ShowDates,
val => val.show.ShowID,
showdate => showdate.ShowID,
(val, showDate) => new { val.show, val.venue, showDates = showDate })
.GroupBy(u => u.show.ShowID)
.Select(grp => new
{
showId = grp.Key,
name = grp.FirstOrDefault()?.show.showName,
venue = grp.FirstOrDefault()?.venue.VenueName,
startDate = grp.Max(g => g.showDates.DateTime)
});
we need to now realation beetwen them one to one or one to many , but not too far from this answer.
var GrouppedResult = Shows.Include(x=>x.Veneu).Include(x=>x.ShowDates)
.Where(x=>x.Veneu.Any()&&x.ShowDates.Any())
.GroupBy(x=>x.ShowId)
.Select(x=>///anything you want);
or
from show in Shows
join veneu in Veneu on veneu.VeneuId equals show.VeneuId
join showDates in ShowDates on showDates.ShowId=show.ShowID
group show by show.Id into grouppedShows
select new { ///what you want };
I'm trying to do something very simple.
I have two tables in my database that I would like to query using linq.
Table of Books, and table of GenreTypes. The result of this query would go to my web Api.
Here is a code snippet:
public List<BooksChart> GetBooksChart()
{
var results = from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select (z => new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
return results;
}
public class BooksChart
{
public string category_name;
public int value;
}
The results of the grouping "n" I would like to store them in BooksChart class to construct the Api.
This code is not compiling.
Previously, I was querying only one table of Books which I have divided into Books and GenreTypes.
My previous working code for querying Books was :
var results = _dbcontext
.Books
.GroupBy(x => x.GenreType)
.Select(z => new BooksPieChart
{
category_name = z.Key,
value = z.Count()
}).ToList();
return results;
EDIT
What I want to achieve in SQL is the following:
select count(*), g.Name
from books b, GenreTypes g
where b.GenreTypeId = g.Id
group by g.Name;
You are mixing the two syntax options of query and method. For query syntax you need to do the projection (select) like this:
return (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart {
category_name = n.Key,
value = n.Count()
}).ToList();
The format of (z =>....) is the declaration of the labmda passed to the Select method.
Site notes:
As #Rabbi commented, since you are using EF, consider properly defining navigation properties. It will make querying simpler.
Side note for the sql - consider using joins instead of multiple tables in the from: INNER JOIN ON vs WHERE clause
The parentheses must surround the whole query, like so:
var results = (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
The compilation error is due to this (z => which is not needed at all.
I have this query
return _ctx.TestPackages.Where(s => s.Id == TestPackageId).
Join(_ctx.TestPackageReportDetails, s => s.Id, d => d.TestPackageId, (s, d) => new { reportDetail = d, testpack = s }).
Join(_ctx.TestPackageReports, p => p.reportDetail.TestPackageReportId, o => o.Id, (p, o) => new { combined = p, report = o })
.ToList()
As you can see my query makes join between 3 tables TestPackages TestPackageReportDetails and TestPackageReports. When I have more than one record in TestPackageReportDetails with same testpackageid, the result is repeated 3 times in the output. How can I avoid the repetition?
Should I make a left join between TestPackageReportDetails and TestPackages? If yes how can I do that?
If your intention is to Eager load the report details then you should be using .Include :
_ctx.TestPackages.Include(t=>t.TestPackageReportDetails.TestPackageReports).Where(s => s.Id == TestPackageId);
Since you're only selecting three fields (As shown before you edit your answer again and remove the select) then you can do this:
(from s in _ctx.TestPackages
join d in _ctx.TestPackageReportDetails,
on s.Id equals d.TestPackageId
join r in _ctx.TestPackageReports
on s.Id equals r.reportDetail.TestPackageReportId
where s.Id == TestPackageId
select new
{
s.Id,
s.packageNumber,
s.Size,
s.TestPackageOrder
}).Distinct().ToList().Select(m=> new ..) // continue your normal selection
I have an existing (working!) linq expression:
from ca in db.CustomAnswer
join ss in db.SurveySubmission on ca.SubmissionId equals ss.Id
join cq in db.CustomQuestion on ca.QuestionId equals cq.Id
where (ss.SurveyId == request.SurveyId)
orderby ss.Submitted, cq.SortOrder
select new
{
SubmissionId = ss.Id,
Answer = ca.Answer
}
I want to add the index of the select into the new object, e.g.
from ca in db.CustomAnswer
join ss in db.SurveySubmission on ca.SubmissionId equals ss.Id
join cq in db.CustomQuestion on ca.QuestionId equals cq.Id
where (ss.SurveyId == request.SurveyId)
orderby ss.Submitted, cq.SortOrder
select new
{
SubmissionId = ss.Id,
**Code = selectIndex,**
Answer = ca.Answer
}
To do this, I believe I need to first convert my query to method syntax so I can use the Select((q, index) => ...) form. To my simple mind, I think it should be:
db.SurveySubmission
.Where(ss => ss.SurveyId == request.SurveyId)
.OrderBy(ss => ss.Submitted)
.Join(db.CustomAnswer, ss => ss.Id, ca => ca.SubmissionId, (ss, ca) => new { ss, ca })
.Join(db.CustomQuestion, o => o.ca.QuestionId, cq => cq.Id, (o, cq) => new { o.ss, o.ca, cq })
.OrderBy(q => q.cq.SortOrder)
.Select((q, idx) => new {
SubmissionId = q.ss.Id,
Answer = q.ca.Answer,
Code = idx
});
However, when the expression is evaluated I get an error:
LINQ to Entities does not recognize the method
'System.Linq.IQueryable1[<>f__AnonymousTypef3[System.Guid,System.String,System.Int32]]
Select[<>f_AnonymousTypee3,<>f__AnonymousTypef3]
(System.Linq.IQueryable1[<>f__AnonymousTypee3[My.Data.Namespace.SurveySubmission,
My.Data.Namespace.CustomAnswer,My.Data.Namespace.CustomQuestion]],
System.Linq.Expressions.Expression1[System.Func3[<>f_AnonymousTypee3[My.Data.Namespace.SurveySubmission,
My.Data.Namespace.CustomAnswer,My.Data.Namespace.CustomQuestion],System.Int32,<>f__AnonymousTypef3[System.Guid,
System.String,System.Int32]]])'
method, and this method cannot be translated into a store expression.
I'm hoping this is glaringly obvious to someone? I've stared at it for several hours and the only conclusion I can make is that I'm not clever enough ... can anyone help please??
EF can't translate that into SQL, because in SQL sets are unordered; the idea of an index just doesn't make any sense to it.
Instead do everything but getting the index using an EF query, and then tack on the indexes in a linq to objects query:
var query = //your original query goes here
var finalQuery = query.AsEnumerable()
.Select((answer, index) => new
{
answer.SubmissionId,
answer.Answer,
Code = index,
});
I would like to create a LINQ join statement equivalent of a Left Join
My tables are set up like so:
Recipe
RecipeID
...
Instruction
RecipeID
StepID
SomeFlag
...
Equivalent SQL:
SELECT *
FROM Recipe r
LEFT JOIN Instruction i
ON r.RecipeID = i.RecipeID
AND SomeFlag > 0
This is what I have so far:
var tmp = db.Recipe
.GroupJoin(
db.Instruction,
r => r.RecipeID,
i => i.RecipeID,
(r, i) => new {r, i},
???);
Firstly, is GroupJoin the correct choice for this type of operation? From what I understand, Join is equivalent to the SQL 'Inner Join' and GroupJoin is equivalent to 'Left Join'. Second, what is the correct syntax to obtain my desired result? I have been searching for a while and I can't seem to find a suitable answer using extension methods.
Don't forget to read the help from (GroupJoin: MSDN http://msdn.microsoft.com/en-us/library/bb535047.aspx and Join MSDN http://msdn.microsoft.com/fr-fr/library/bb534675.aspx)
The last argument of the GroupJoin and Join is optional (by overload) and is not usually used.
It is a function that allow you to specify how to compare r.RecipeID with i.RecipeID. As RecipeID must be an integer, using the default comparer is a good choice. So let it with:
var tmp = db.Recipe
.Join(db.Instruction,
r => r.RecipeID,
i => i.RecipeID,
(r, i) => new {r, i});
Now what you want to have is to remove all the instructions that have SomeFlag > 0. Why not do this before joining?
Like this:
var tmp = db.Recipe
.Join(db.Instruction.Where(instruction => instruction.SomeFlag > 0),
r => r.RecipeID,
i => i.RecipeID,
(r, i) => new {r, i});
Update
#usr has perfectly commented saying Join performs an INNER JOIN.
As you may have remarked, LINQ does not have different methods for INNER, OUTER, LEFT, RIGHT joins. To know the equivalent LINQ of a particular SQL join you may find help on MSDN ( http://msdn.microsoft.com/en-us/library/vstudio/bb397676.aspx ).
var tmp = from recipe in Recipes
join instruction in
from instruction in Instructions
where instruction.SomeFlag > 0
select instruction
on recipe.RecipeID equals instruction.RecipeID into gj
from instruction in gj.DefaultIfEmpty()
select new
{
recipe,
instruction
};
using extension methods it is a bit of an ugly solution:
var tmp = Recipes.GroupJoin(Instructions.Where(instruction => instruction.SomeFlag > 0),
recipe => recipe.RecipeID,
instruction => instruction.RecipeID,
(recipe, gj) => new { recipe, gj })
.SelectMany(#t => #t.gj.DefaultIfEmpty(),
(#t, instruction) => new
{
#t.recipe,
instruction
});
Please tell me if I did't understand you, but this extension method returns the same result that you priveded in sql.
public static IEnumerable<ResultType> GetLeftJoinWith(this IEnumerable<Recipe>, IEnumerable<Instructions> ins)
{
var filteredInstructions = ins.Where(x => x.SomeFlag > 0);
var res = from r in rec
join tmpIns in filteredInstructions on r.RecipeID equals t.RecipeID into instructions
from instruction in instructions.DefaultIfEmpty()
select new { r, instruction };
return res;
}
try this
var model = db.Recipe
.GroupJoin(db.Instructions.Where(instruction => instruction.SomeFlag > 0),r => r.RecipeID,i => i.RecipeID, (r, i) => new { Recipe = r, Instructions = i })
.SelectMany(t => t.Instructions.DefaultIfEmpty(),(t, Instructions) => new
{
Recipe = t.Recipe,
Instructions = Instructions
});