views:

186

answers:

4

Is it possible to accomplish something like this using linqtosql?

select * from table t1
left outer join table2 t2 on t2.foreignKeyID=t1.id
left outer join table3 t3 on t3.foreignKeyID=t1.id

I can make it work using both DataLoad options or join syntax. But the problem is whenever I add a second left join, linqtosql queries with MULTIPLE sql statements, instead of doing a second left join in the underlying sql.

So a query like the one above will result in dozens of sql calls instead of one sql call with 2 left joins.

What are my other options? I can use a view in the DB, but now I'm responsible for creating the hierarchy from the flattened list, which is one of the reasons to use an ORM in the first place.

Note that T2 and T3 are 1:M relationships with T1. Is it possible to have linq efficiently query these and return the hierarchy?

+1  A: 

Here is a similar question. Be cognizant of how the joins are composed.

For example, the following Linq to Sql query on AdventureWorks:

AdventureWorksDataContext db = new AdventureWorksDataContext();

var productStuff = from p in db.Products
                   join pl in db.ProductListPriceHistories on p.ProductID equals pl.ProductID into plv
                   from x in plv.DefaultIfEmpty()
                   join pi in db.ProductInventories on p.ProductID equals pi.ProductID into pii
                   from y in pii.DefaultIfEmpty()
                   where p.ProductID == 764
                   select new { p.ProductID, x.StartDate, x.EndDate, x.ListPrice, y.LocationID, y.Quantity };

Yielded the same SQL (verified via Profiler) as this SQL query:

SELECT Production.Product.ProductID, 
       Production.ProductListPriceHistory.StartDate,
       Production.ProductListPriceHistory.EndDate,
       Production.ProductListPriceHistory.ListPrice,
       Production.ProductInventory.LocationID,
       Production.ProductInventory.Quantity
FROM Production.Product
LEFT OUTER JOIN Production.ProductListPriceHistory ON Production.Product.ProductID = Production.ProductListPriceHistory.ProductID 
LEFT OUTER JOIN Production.ProductInventory ON Production.Product.ProductID = Production.ProductInventory.ProductID 
WHERE Production.Product.ProductID = 764

Multiple LEFT JOINs on the Primary Key of the parent table, yielding one generated SQL query.

Forgotten Semicolon
That's not applicable.Notice that both my left joins are joining to the same parent table (T1). I'm not trying to left join T2 into T3 and return the results from T1, T2 and T3.T1 has 1:M relationships with several tables.I'm trying to return the children of T1 via multiple Left joins without incurring one sql call per parent element for all left joins after the first one.
Scott
A: 

I would think LINQ to SQL would be able to translate your left outer joins as long as you put the DefaultIfEmpty() calls in the right places:

var q = from t1 in table
        join t2 in table2 on t1.id equals t2.foreignKeyID into j2
        from t2 in j2.DefaultIfEmpty()
        join t3 in table3 on t1.id equals t3.foreignKeyID into j3
        from t3 in j3.DefaultIfEmpty()
        select new { t1, t2, t3 };
dahlbyk
Yes it can translate. But it translates it into dozens of sql calls if I join more than one table.
Scott
A: 

I don't think this is likely to be the right solution to your problem because there are more than one many to one relationships to your parent entity table:

select * from table t1 
left outer join table2 t2 on t2.foreignKeyID = t1.id 
left outer join table3 t3 on t3.foreignKeyID = t1.id

This is like a person with multiple children and multiple vehicles:

Say t1 is the person

id             str
1              Me

Say t2 are the children

PK  foreignKeyID   str
A   1              Boy
B   1              Girl

Say t3 are the vehicles

PK  foreignKeyID   str
A   1              Ferrari
B   1              Porsche

Your result set is:

Me Boy Ferrari
Me Girl Ferrari
Me Boy Porsche
Me Girl Porcshe

Which I fail to see how this is a useful query (even in SQL).

Cade Roux
Ya, you might be right. I guess what I'm trying to accomplish is a deep load of the parent entity without multiple hits to the database. I have a view that currently returns the results you describe and was just hoping I could make linq create the hierarchy for me. But I think that is wishful thinking.See, when you get into more complex result sets I fail to see how Linq syntax is beneficial. It's harder to understand than just putting sql in a view, in my opinion. The only advantage was that if linq could magically build the hierarchy. Absent that, I'm going to revert to sql views.
Scott
I'm not a heavy LINQ2SQL user. For deep loads of object hierarchies we use stored procs that return multiple recordsets and then use NextRecordset to populate the entity object's child collections (unless lazy loading, of course). If you are trying for magic, you might want to look into an ORM.
Cade Roux
Thanks Cade, that's my thinking too. I think sprocs would solve some of the problem. But, I can accomplish a similar thing using Linq. Grab just the root entities. Then in a 2nd query join to the children I want. In a 3rd query join to the other children. Then put it together in memory.This way, I reduce the DB calls to 3, and still get all the advantages of linq.The current approach ends up querying the DB once for each child. So in a page with 100 records, you get 100 queries just to load the second collection of children, instead of 3. Sprocs=1 query, but then I lose linqiness :)
Scott
I guess my thinking was flawed. Any performance advantage of reducing 3 queries to 1, is LOST by the fact that I'm returning hundreds of rows of redundant data unnecessarily due to the multiple left joins. :). I must need more sleep.
Scott
A: 

You dont need that terrible join syntax if you FK's are properly setup.

You would probably write:

var q = from t1 in dc.table1s
        from t2 in t1.table2s.DefaultIfEmpty()
        from t3 in t1.table3s.DefaultIfEmpty()
        select new { t1, t2, t3 };
leppie