Selective Linq child include two levels deep - linq

I'm porting some sql stored procedure logic, which would return multiple tables in a dataset, to entity framework strongly typed objects, queried with linq.
Basically I need the data from tables A, B, and C, where C has a foreign key to B, and B has a foreign key to A. But I don't want every C with a FK to B, just the C's with a certain constraint X.
So basically, the stored proc basically said
TableA = select from A where A.AID = AIDPassedIn
TableB = select from B where B.AID = AIDPassedIn
TableC = select from TableB where TableB.XID = XIDPassedIn
return new DataSet(TableA, TableB, TableC);
//yes this is gross and confusing, thus our current efforts
Entity framework almost makes this super easy like so
A.Include("B.C").Where(a => a.AID == AIDPassedIn)
My only problem is that this doesn't include constraint X on the C table. I've read a bunch of articles, but everything I've read suggests things I could add to the where clause, and that would filter what A objects I end up with. I should only end up with one A object though, regardless of the properties of it's children. What I want is The A with AIDPassedIn, and all it's child B's, and all the B's children C that match constraint X.
I feel like this is one of my worst phrased questions ever but I'm at a bit of a block. Any help would be great thanks!

You can try it along the lines of the following:
var AList = context.As.Where(a => a.AID == AIDPassedIn)
.Select(a => new
{
A = a,
Bs = a.Bs,
Cs = a.Bs.Select(b => b.Cs.Where(c => c.XID == XIDPassedIn))
})
.AsEnumerable()
.Select(x => x.A)
.ToList(); // or SingleOrDefault if AIDPassedIn is the PK
Entity Framework will put the object graph together automatically (even without using Include) as long as you don't disable change tracking.

Related

Linq2Entities Equivalent Query for Parent/Child Relationship, With All Parents and Children, Filtering/Ordering Children

So the question is ridiculously long, so let's go to the code. What's the linq2entities equivalent of the following Sql, given entities (tables) that look like:
Parent
---
parent_id
parent_field1
Child
--
child_id
parent_id
child_field1
child_field2
The sql:
select p.*, c.*
from parent p
inner join p on
p.parent_id = child.parent_id
where
c.child_field1 = some_appropriate_value
order by
p.parent_field1
c.child_field2
L2E let's you do .include() and that seems like the appropriate place to stick the ordering and filtering for the child, but the include method doesn't accept an expression (why not!?). So, I'm guessing this can't be done right now, because that's what a lot of articles say, but they're old, and I'm wondering if it's possible with EF6.
Also, I don't have access to the context, so I need the lambda-syntax version.
I am looking for a resultant object hierarchy that looks like:
Parent1
|
+-- ChildrenOfParent1
|
Parent2
|
+-- ChildrenOfParent2
and so forth. The list would be end up being an IEnumerable. If one iterated over that list, they could get the .Children property of each parent in that list.
Ideally (and I'm dreaming here, I think), is that the overall size of the result list could be limited. For example, if there are three parents, each with 10 children, for a total of 33 (30 children + 3 parents) entities, I could limit the total list to some arbitrary value, say 13, and in this case that would limit the result set to the first parent, with all its children, and the second parent, with only one of its children (13 total entities). I'm guessing all of this would have to be done manually in code, which is disappointing because it can be done quite easily in SQL.
when you get a query from db using entityframewrok to fetch parents, parent's fields are fetched in single query. now you have a result set like this:
var parentsQuery = db.Parents.ToList();
then, if you have a foreign key on parent, entityframework creates a navigation property on parent to access to corresponding entity (for example Child table).
in this case, when you use this navigation property from parent entities which already have been fetched, to get childs, entityframework creates another connection to sql server per parent.
for example if count of parentsQueryis 15, by following query entityframework creates 15 another connection, and get 15 another query:
var Childs = parentsQuery.SelectMany(u => u.NavigationProperty_Childs).ToList();
in these cases you can use include to prevent extra connections to fetch all childs with its parent, when you are trying to get parents in single query, like this:
var ParentIncludeChildsQuery = db.Parents.Include("Childs").ToList();
then by following Query, entityframework doesn't create any connection and doesn't get any query again :
var Childs = ParentIncludeChildsQuery.SelectMany(u => u.NavigationProperty_Childs).ToList();
but, you can't create any condition and constraint using include, you can check any constraint or conditions after include using Where, Join, Contains and so forth, like this:
var Childs = ParentIncludeChildsQuery.SelectMany(u => u.NavigationProperty_Childs
.Where(t => t.child_field1 = some_appropriate_value)).ToList();
but by this query, all child have been fetched from database before
the better way to acheieve equivalent sql query is :
var query = parent.Join(child,
p => p.ID
c => c.ParentID
(p, c) => new { Parent = p, Child = c })
.Where(u => u.Child.child_field1 == some_appropriate_value)
.OrderBy(u => u.Parent.parent_field1)
.ThenBy(u => u.Child.child_field2)
.ToList();
according to your comment, this is what you want:
var query = parent.Join(child,
p => p.ID,
c => c.ParentID,
(p, c) => new { Parent = p, Child = c })
.Where(u => u.Child.child_field1 == some_appropriate_value)
.GroupBy(u => u.Parent)
.Select(u => new {
Parent = u.Key,
Childs = u.OrderBy(t => t.Child.child_field2).AsEnumerable()
})
.OrderBy(u => u.Parent.parent_field1)
.ToList();

How can I select items from a table but ban certain from another?

I have two tables, one contains entities other entitylog.
MyEntity:
id, lat, lon
A entity has a position in the world.
MyEntityLog:
id, otherid, otherlat, otherlon
Entity with id has interacted with otherid at otherid's latitude and longitude.
For instance, I have the following entities:
1, 4.456, 2.234
2, 3.344, 6.453
3, 6.234, 9.324
(not very accurate, but it serves the purpose).
Now, If entity 1 interact with 2 the result on the log table would look like:
1, 2, 3.344, 6.453
So my question is, how can I for listing entity 1's available interactions NOT include the ones on the log table?
The result of listing entity 1's available interactions should be only be entity 3 as it already has a interaction with 2.
First make a list of ids that interact with entity 1:
var id1 = 1;
var excluded = from l in db.EntityLogs
where l.id == id1
select l.otherid;
then find the entries not having an id in this list or equal to id1:
var logs= from l in db.EntityLogs
where !excluded.Contains(l.id) && l.id != id1
select l;
Note that linq will defer the execution of excluded and incorporate it in the execution of logs.
Not sure if I understand your question, I guess I need more details, but if you want to list the entities that have no entry in log table, one solution will be something like this, assuming myEntities is the collection of MyEntity and myEntityLogs is the collection of MyEntityLog
var firstList = myEntities.Join(myEntityLogs, a => a.Id, b => b.Id, (a, b) => a).Distinct();
var secondList = myEntities.Join(myEntityLogs, a => a.Id, b => b.OtherId, (a, b) => a).Distinct();
var result = myEntities.Except(firstList.Concat(secondList)).ToList();

ef and linq extension method

I have this sql that i want to have written in linq extension method returning an entity from my edm:
SELECT p.[Id],p.[Firstname],p.[Lastname],prt.[AddressId],prt.[Street],prt.[City]
FROM [Person] p
CROSS APPLY (
SELECT TOP(1) pa.[AddressId],a.[ValidFrom],a.[Street],a.[City]
FROM [Person_Addresses] pa
LEFT OUTER JOIN [Addresses] AS a
ON a.[Id] = pa.[AddressId]
WHERE p.[Id] = pa.[PersonId]
ORDER BY a.[ValidFrom] DESC ) prt
Also could this be re-written in linq extension method using 3 joins?
Assuming you have set the Person_Addresses table up as a pure relation table (i.e., with no data besides the foreign keys) this should do the trick:
var persons = model.People
.Select(p => new { p = p, a = p.Addresses.OrderByDescending(a=>a.ValidFrom).First() })
.Select(p => new { p.p.Id, p.p.Firstname, p.p.LastName, AddressId = p.a.Id, p.a.Street, p.a.City });
The first Select() orders the addresses and picks the latest one, and the second one returns an anonymous type with the properties specified in your query.
If you have more data in your relation table you're gonna have to use joins but this way you're free from them. In my opinion, this is more easy to read.
NOTE: You might get an exception if any entry in Persons have no addresses connected to them, although I haven't tried it out.

How to write linq to get data from different tables?

Suppose I have 3 tables: A(aid, ...), B(bid, ...) Relationship(aid, bid, ...)
Then I have a aid as parameter, I want to get the result from b. If use SQL, will be like
select b.* from B join Relationship on b.bid = Relationship.bid
where relationship.aid = aid_param
how to write linq with same result as above SQL. (the foreign key was not created correctly in this case)?
Assuming that you've used the designer to add your tables to the LINQ data context and you have either foreign key relationships defined in the database or have hand-coded the associations in the designer, you should simply be able to reference the B table and it's EntitySet of Relationship properties, filtered by your parameter, and check if there are any matches.
var relationships = db.B.Any( b => b.Relationships.Where( r => r.aid == aid_param ) );
var bQuery = from r in MyContext.Relationship
where r.aid == aid_param
select r.B;
If you don't have a relationship set in the model, then u can:
var bQuery = from b in myContext.B
where myContext.Relationship
.Any( r => r.aid == aid_param && b.bid == r.bid)
select b;
var output =
from b
from Relationship
where (b.bid = Relationship.bid,
relationship.aid = aid_param)

How to query (LINQ) multiple table association link?

I have tables association such as (CaseClient is a bridge table):
Cases has many CaseClients
Client has many CaseClients
ClientType has many CaseClient
The easiest way just use the view in database but I heard that with linq you can join this somehow? Or should I just created view in the database and linq query agains that view?
I am appreciated your comment
I think you want to use the Join method, from your bridging table and resolving each of your relationships. E.g.
// Where CaseId and TypeId are your members of CaseClient
var x = caseClients.Join( cases, cc => cc.CaseId, c => c.Id)
.Join( types, cc => cc.TypeId, t => t.Id)
.Select();
Above code untested (so far) and from memory. You may need to put a Select between the two joins.
Heres an adaptation of what I did for a very similar situation. Only the names have been changed to protect the innocent.
IEnumerable<Case> getCaseByClient(int client_id)
{
var ret = from c in Cases
join cc in CasesClients
on c.Id equals cc.ClientId
join cl in Clients
on cc.ClientId equals client_id
select c;
return ret;
}
of course this assumes your client_id field is an int, but thats easy enough to modify.

Resources