+1  A: 

This StackOverflow post might provide some insight:

http://stackoverflow.com/questions/236575/linq-to-sql-hierarchical-query-to-find-ancestors

Robert Harvey
Yea, I had read through that question before asking this one. I'm having trouble relating it to mine.
KingNestor
A: 

If you are doing this with direct LINQ to SQL, there is no way to do this without some kind of recursion, whether you do it yourself or you hide it behind an extension method. Recursive SQL is very bad (many round trips, many single queries).

There are two options here. One is to pull the entire table(s) with the hierarchy into memory and use LINQ to Objects on it. Leave the "details" tables in SQL. If you have less than several thousand entities, this is probably the most efficient way to go. You can keep a single copy of the table(s) in cache and refresh them when necessary. When you need to fetch more detailed data from the DB for a single record, you can reattach that entity from your cached hierarchy to a new DataContext and fetch it.

The other option is to use a more complex relationship model in your database. Storing parent only by nature demands recursion, but you can use the adjacency list model to construct a single query which can span many levels of inheritance. This will mean your LINQ to SQL queries become less intuitive (querying against Entity.Right and Entity.Left isn't quite as pretty as Parent or Children...) but you can do in one query what might take hundreds or thousands in the literal recursive approach.

Rex M
+3  A: 

If you drop all four tables (Agency, BusinessUnit, Client, Map) on the linq to sql designer, and draw relationships from Map to the other three, there will be some useful properties on Map.

  //construct a query to fetch the row/column shaped results.
var query = 
  from m in db.map
  //where m.... ?
  let a = m.Agency
  let b = m.BusinessUnit
  let c = m.Client
  // where something about a or b or c ?
  select new {
    AgencyID = a.AgencyID,
    AgencyName = a.Name,
    BusinessUnitID = b.BusinessUnitID,
    ClientID = c.ClientID,
    NumberOfAccounts = c.NumberOfAccounts,
    Score = c.Score
  };
  //hit the database
var rawRecords = query.ToList();

  //shape the results further into a hierarchy.    
List<Agency> results = rawRecords
  .GroupBy(x => x.AgencyID)
  .Select(g => new Agency()
  {
    Name = g.First().AgencyName,
    BusinessUnits = g
    .GroupBy(y => y.BusinessUnitID)
    .Select(g2 => new BusinessUnit()
    {
      Clients = g2
      .Select(z => new Client()
      {
        NumberOfAccounts = z.NumberOfAccounts,
        Score = z.Score
      })
    })
  })
  .ToList();

If approriate filters are supplied (see the commented out where clauses), then only the needed portions of the tables will be pulled into memory. This is standard SQL joining at work here.

David B
@David B, This looks really promosing! I'm curious, can you elaborate on the commented out where statements? What condition should I have in place, given my requirements do you think?
KingNestor
If you really need "All" agencies, then there should be no filtering. Usually one would not need all the data in the database, and so any criteria you can supply will limit the records read and transferred.
David B
@David B, gotchya. I appreciate your answer very much!
KingNestor
@David B, when I type out the first part of your query: from a in m.Agency, I get an immediate error stating, "An expression of type 'DomainModel.Agency' is not allowed in a subsequent from clause in a query expression with source type Linq.Table<DomainModel.Bac_Map>. Type inference failed in the call to "Select Many"
KingNestor
@David B, can you verify your linq expression syntax?
KingNestor
@David B, +1, but I also tried it and got something similar. I could of been doing something wrong though, I'm not a LINQ ninja.
Simucal
Quite right. m.Agency is not a collection and cannot be queried. Editted Answer.
David B
@David B, thank you for the update. Now, lets suppose Agency and Business Unit had more members than what is shown above. Should the first query: "select new { Agency = a, BusinessUnit = b, Client = c }; or not? I was thinking that if it returned the actual objects themselves that in the second expression that shapes the records we could maintain all its members?
KingNestor
@David B, or do they have to be flat, where in the first query I have to specify AgencyName, BusinessUnitName, AgencyCreatedDate, BusinessUnitCreatedDate, etc?
KingNestor
I guess my question is, how do I include a property in one of those objects like Agency further up the chain? For example, if Agency contained a "Name" and also a IEnumberable<BusinessUnit>'s?
KingNestor
"select new { Agency = a, BusinessUnit = b, Client = c };" Looking at jrista's answer, it seems this might work. Give it a shot. If it doesn't work out, you can always fall back to the form I have here, with "AgencyName = a.Name"
David B
@David B, Lets say I went with the AgencyName route, your way. How would I access AgencyName in the select new Agency() {} portion of your second query? You are grouping based off AgencyID, which I get, but how do I go from that grouping, "g", to access the AgencyName which I include from the first query?
KingNestor
Editted answer to show fetching and shaping of AgencyName.
David B
@David B, you are a champion among men. Thanks a lot.
KingNestor
+2  A: 
jrista