Orderby() in tri-level entity structure - linq

I have 3 entities that all encompass eachother. MakeSource can have many ModelSources and ModelSources can have many YearSources. The data I am retrieving looks like this:
So far I can only order by Makes and not models or years.
var data = context.MakeSources.OrderBy(P => P.Name).Include(A => A.ModelSources
.Select(B => B.YearSources));
Placing more OrderBy calls within the include throws errors. My question is:
How can I get a neatly ordered collection of first Makes ordered by asc, for each make its models ordered by ascending and for each model its years ordered by ascending?
I tried using ThenBy() but then I cannot access nested properties of ModelSource
var data = context.MakeSources.Include(A => A.ModelSources
.Select(B => B.YearSources)).OrderBy(P => P.Name).ThenBy(p => p.ModelSources.

I re-created your situation in my sandbox database, and wrote a linq query like this. This gives you a list of makes, models, and years. I used cars, but i really don't know what your data looks like.
var data = (from a in ctx.MakeSources
join b in ctx.ModelSources on a.Id equals b.MakeSourceId
join c in ctx.YearSources on b.Id equals c.ModelSourceId
orderby a.Name, b.Name, c.Year
select new { Make = a.Name, Model = b.Name, Year = c.Year }).ToArray();
This will give you a flat list
To order them in collections, such as each make has a collection of models, and models have a collection of years, I did this.
var data = (from a in ctx.MakeSources
orderby a.Name
select new
{
Make = a.Name,
Models = (from b in ctx.ModelSources
where b.MakeSourceId == a.Id
orderby b.Name
select new
{
Model = b.Name,
Years = (from c in ctx.YearSources
where c.ModelSourceId == b.Id
orderby c.Year
select new { ModelYear = c.Year })
})
}).ToArray();
I am not aware of any easy way to do it using just includes and .OrderBy()s as it preloads the navigation properties, and the result in the include method has to be a collection of entities, as far as I am aware. So returning an ordered list won't load the entities (as evident by the error you received)

Related

Linq Left Outer Join Two Tables on Two Fields

How do I left outer join two tables on two fields in linq?
I have a sql:
select a.*, b.* from courselist as a
left outer join Summary as b
on a.subject = b.Subject and a.catalog =
b.Catalogno
where a.degree_id = 1
order by a.sequenceNo
Below is my linq query, but there is error underline "join", failed in the call to "Groupjoin". I don't know how to correct that.
var searchResults = (from a in db.courselist
join b in db.Summary on
new { a.subject,a.catalog } equals
new { b.Subject, b.Catalogno } into ab
where a.degree_id == 1
orderby a.degree_sequenceNo
from b in ab.DefaultIfEmpty()
select new
{
Courselist = a,
Summary = b
}
).ToList();
Thanks.
I've checked your code again,
I found it's fault
you just need to specify join parameters name like this:
new { suject = a.subject, catalog = a.catalog } equals
new { suject = b.subject, catalog = b.Catalogno } into ab
It seems you are missing the reference, the query doesn't have an error
try to use this:
using System.Linq;
The main issue when people start using LINQ is that they keep thinking in the SQL way, they design the SQL query first and then translate it to LINQ. You need to learn how to think in the LINQ way and your LINQ query will become neater and simpler. For instance, in your LINQ you don't need joins. You should use Associations/Navigation Properties instead. Check this post for more details.
There should be a relationship between courselist and Summary, in which case, you can access Summary through courselist like this:
var searchResults = (from a in db.courselist
where a.degree_id == 1
orderby a.degree_sequenceNo
select new {
Courselist = a,
Summary = a.Summary
}).ToList();
If there is no relationship between the two, then you should reconsider your design.

How do I outer join and group by in Entity framework Linq?

I'm having trouble getting my Linq statemnt to work when doing an outer join and a group by. Here's a SQL version of what I'm trying to accomplish:
select p.PRIMARY_KEY, min(p.EFFECTIVE_DATE), sum(IsNull(c.PAID_INDEMNITY, 0))
from PRMPOLCY p
left outer join CLMMAST c on p.PRIMARY_KEY = c.POLICY_NO
where p.UNDERWRITER_UID = 93
GROUP BY p.PRIMARY_KEY
Here's what I have in Linq (which doesn't work):
var result = from p in context.PRMPOLCies
join c in context.CLMMASTs on p.PRIMARY_KEY equals c.POLICY_NO into polClm
where (p.UNDERWRITER_UID == underwriter)
from grp in polClm.DefaultIfEmpty()
group grp by p.PRIMARY_KEY into g
select new PolicySummation()
{
PolicyNo = g.Key,
Incurred = g.Sum(grp => grp.PAID_INDEMNITY ),
EffDate = g.Min(grp => grp.PRMPOLCY.EFFECTIVE_DATE
};
Beating my head against the wall trying to figurwe this out!
Assuming you have a navigation property set up between PRMPOLCY and CLMMAST, you shouldn't need to specify the join explicitly. It's much easier to express most queries in linq without explicit joins, but rather treating your structures as a hierarchy. I don't know the specifics of your model property names, but I'd take a guess that something like this would work.
var result =
from p in context.PRMPOLCies
where (p.UNDERWRITER_UID == underwriter)
select new PolicySummation {
PolicyNo = p.PRIMARY_KEY,
Incurred = p.CLMASTs.Select(c => c.PAID_INDEMNITY).DefaultIfEmpty().Sum(),
EffDate = p.EFFECTIVE_DATE,
};
You need to include both your tables in the group clause like this:
group new { p, grp } by p.PRIMARY_KEY into g
Then in your Sum / Min
g.Sum(grp => grp.grp == null ? 0 : grp.grp.PAID_INDEMNITY )
g.Min(grp => grp.p.PRMPOLCY.EFFECTIVE_DATE)

EF Linq query with conditional include

So I have the following Linq query:
var member = (from mem in
context.Members.Include(m =>
m.MemberProjects.Select(mp => mp.Project))
where mem.MemberId == memberId
select mem).FirstOrDefault();
This returns a Member entity, with a set of MemberProjects that have a Project child. I would like to limit the MemberProjects to only those for which the Project child has a property
ProjectIdParent == null.
One of my failed attempts might make the intent clearer:
var member = (from mem in context.Members
.Include(m => m.MemberProjects
.Where(mp =>
mp.Project.ProjectIdParent == null)
.Select(proj => proj.Project))
where mem.MemberId == memberId
select mem).FirstOrDefault();
This of course complains of an invalid Include expression because of the Where clause.
Any thoughts on how to do this would be great :)
DISCLAIMER: I havent tested this. This is just an idea. If you let me know the results, I will update this accordingly. (Skip to the update part for the tested solutions)
var member = (from mps in context.MemberProjects
.Include(m => m.Members)
.Include(m => m.Projects)
where mps.Project.ProjectIdParent == null
select mps)
.FirstOrDefault(mprojs => mprojs.Member.MemberId == memberId);
I'd also analyze the queries using something like EFProfiler to make sure the generated queries dont leave the realm of sanity.
You can also take a look at this post by Jimmy Bogard on Many to Many relationships with ORMs.
Update
I came up with multiple tested solutions for this with EF 6.1.3. My Edmx looked like below:
The setup data is like below:
I was able to run code below to get the MemberFive correctly
var member = context.Members.FirstOrDefault
(m => m.MemberId == memberId
&& m.Projects.Any(p => p.ProjectParentId == null));
The generated SQL looked like this:
SELECT TOP (1) [Extent1].[MemberId] AS [MemberId],
[Extent1].[MemberName] AS [MemberName]
FROM [dbo].[Members] AS [Extent1]
WHERE ([Extent1].[MemberId] = 1)
AND (EXISTS (SELECT 1 AS [C1]
FROM (SELECT [MemberProjects].[MemberId] AS [MemberId],
[MemberProjects].[ProjectId] AS [ProjectId]
FROM [dbo].[MemberProjects] AS [MemberProjects])
AS [Extent2]
INNER JOIN [dbo].[Projects] AS [Extent3]
ON [Extent3].[ProjectId] = [Extent2].[ProjectId]
WHERE ([Extent1].[MemberId] = [Extent2].[MemberId])
AND ([Extent3].[ProjectParentId] IS NULL)))
If you dont like the generated query you can use this:
var memberQuery = #"Select M.* from Members M
inner join MemberProjects MP on M.MemberId = Mp.ProjectId
inner join Projects P on MP.ProjectId = P.ProjectId
where M.MemberId = #MemberId and P.ProjectParentId is NULL";
var memberParams = new[]
{
new SqlParameter("#MemberId", 1)
};
var member3 = context.Members.SqlQuery(memberQuery, memberParams)
.FirstOrDefault();
The later consistently returned under 20ms vs the other one hovered around 60ms (if that matters to you).
I hope this helps.

Filter list having two Tables join data in Entity Framework

I have two tables..
Student (StudentId,Name,FatherName)
Qualification (QualificationId,StudentId,DegreeName)
I have got data like this..
var myList = (from c in entities.Students
join q in entities.Qualifications on c.StudentId equals q.StudentId
select new {c.Name,c.FatherName,q.DegreeName}).ToList();
Now i want to filter myList more.. How can i do it, like..
var filteredList = myList.Select(c=> new Student
{
Name=c.Name,
FatherName=c.FatherName
//Degree=C.Degree
}).ToList();
The above Linq Query is not working if i want to get DegreeName also, My Question is how to further Filter myList.
Thanks.
var filteredList = myList.Where(i => i.FatherName == "Shahid").ToList();
Keep in mind since you called ToList() on the original query you are now filtering in memory. If you want to filter in the database then remove the ToList() on the first query and do it like this:
var myList = from c in entities.Students
join q in entities.Qualifications on c.StudentId equals q.StudentId
select new {
c.Name,
c.FatherName,
q.DegreeName
};
var filteredInDatabase = myList.Where(i => i.FatherName == "Shahid").ToList();

LINQ queries with many-to-many tables in Entity Data Model

I'm trying to use LINQ to query the following Entity Data Model
based on this db model
I'd like to be able to pull a list of products based on ProductFacets.FacetTypeId.
Normally, I'd use joins and this wouldn't be a problem but I don't quite understand how to query many-to-many tables under the Entity DataModel.
This is an example sql query:
select p.Name, pf.FacetTypeId from Products p
inner join ProductFacets pf on p.ProductId = pf.ProductId
where pf.FacetTypeId in(8, 12)
Presuming EF 4:
var facetIds = new [] { 8, 12 };
var q = from p in Context.Products
where p.FacetTypes.Any(f => facetIds.Contains(f.FacetTypeId))
select p;
In EF (assuming the mapping is done correctly), joins are hardly ever used; navigation properties are used instead.
Your original SQL returns a tuple with repeated Name entries. With LINQ, it's often easier
to "shape" the queries into non-tuple results.
The following should be the same as the SQL, only instead of returning (Name, FacetTypeId) pairs with repeated Names, it will return a type that has a Name and a sequence of FacetTypeIds:
var facetIds = new [] { 8, 12 };
var result = from p in db.Products
select new
{
p.Name,
FacetTypeIds = from pf in p.FacetTypes
where pf.FacetTypeId == 8 || pf.FacetTypeId == 12
select pf.FacetTypeId,
};

Resources