A: 

If you have the appropriate relationships set up in the database (and therefore, in the DBML), you may want to consider something like this (UNTESTED):

<table>
<tr>
  <asp:Repeater ID="_users" runat="server">
    <ItemTemplate>
      <td><%# Eval("UserName") %></td>
    </ItemTemplate>
  </asp:Repeater>
</tr>
<asp:Repeater ID="_products" runat="server">
  <ItemTemplate>
    <tr>
      <td><%# Eval("Product.ProductName") %></td>
      <asp:Repeater ID="_users" runat="server" DataSource='<%# GetUsersProducts(Eval("ProductID")) %>'>
        <ItemTemplate>
          <td><%# Eval("count") %></td>
        </ItemTemplate>
      </asp:Repeater>
   </ItemTemplate>
</asp:Repeater>
</table>

Code-behind:

protected void Page_Load(object sender, EventArgs e)
{
   _users.DataSource = context.Users.OrderBy(u => u.UserName);
   _users.DataBind();

   _products.DataSource = context.Products;
   _products.DataBind();
}
protected object GetUsersProducts(string ProductID)
{
  var prodord = from op in context.OrderProducts where op.ProductID == ProductID select op;
  return from u in context.Users.OrderBy(u2 => u2.UserName) select new {
    count = prodord.Where(op => op.Order.User.UserID == u.UserID).Sum(op => op.ProductQty)
  };
}

You will obviously want to change around your markup and some of the object types, but I'm hoping this will get you going. I try to use the autogenerated class object structure as much as possible to avoid creating unreadable LINQ queries.

Keith
The problem with this seems to be that it will run a separate query for every product for every user. If there's 2500 products and 500 users, thats 1.25 million trips to the database... you're going to be waiting a day for the results.
Ryan
+2  A: 

Here's a query that will give you a list of Users, Products, and the Count:

from op in OrderProducts
group op by new {op.Order.User, op.Product} into g
select new
{
  g.Key.User.UserName,
  g.Key.Product.ProductName,
  Count=g.Sum(op => op.ProductQty)
}

It won't give you results in the exact format you want, but it will get you most of the way there using only Linq.

Also take note that this won't pull any extra data - (Users or Products with counts of 0). This may or may not be what you're looking for.

[Edit] Here's another query to take a look at (two actually):

from p in Products
from u in Users
let i = (from op in OrderProducts
         where op.Order.User == u && op.Product == p
      select op.ProductQty)
let quant = i.Count() == 0 ? 0 : i.Sum(v=>v)
select new {p.ProductName, u.UserName, Quantity = quant}
//group new {u.UserName, Quantity = quant} by p into g
//select new {g.Key.ProductName, Users = g.AsEnumerable()}

As is, this should give you straight results (table of ProductName, UserName, Quantity). The commented out portion will instead group by Product, which (depending on how you render your results) might be what you want.

Ryan Versaw
Hmm, this looks promising, but I haven't gotten a chance to mess with it much. How would I modify it to return the zeros? (ie User has ordered this product 0 times). In the end, I will be specifying which products and users to include in the results, so I would have to have those as 0's. Thanks so far though!
Ryan
Also, let me know if anyone has tips for cleaning up my queries!
Ryan Versaw
Hey thanks man! I've been on another project and just got back to this. I have to do this project in VB.NET though, and I have just one issue converting your linq statement to vb.net. I don't know what the i.Sum(v=>v) would convert to. I've got this so far: From p In Products _From u In Users _Let i = (From op In OrderProducts _ Where op.Order.User.UserID = u.UserID And op.Product.ProductID = p.ProductID _ Select op.ProductQty) _Let Qty = _ If(i.Count() = 0, 0, i.Sum()) _Select New With {p.ProductName, u.Username, Qty}Thanks a lot Ryan!
Ryan
Use `i.Sum(Function(v) v))` instead of just the `i.Sum()`. Make sure you take a look at using the groups (commented out portion) as well in case it fits what you need more closely.
Ryan Versaw
Awesome! Thanks so much, I think you've answered a few of my questions on here now, I should just hire you, lol! Thanks a million!
Ryan
I'm happy to help when I can!
Ryan Versaw
I was able to produce a similar "double group by" in T-SQL in a few minutes but it took me quite a long time to figure it out in LINQ. This answer was actually very helpful +1.
jasonco
A: 

The result shape you are asking for is known as a pivot. It has columns which are data-driven, instead of columns which are declared before the query runs.

The trick here is to project the result into a type where the "properties" can vary at run-time. I did something like that here

Here's a stab at your particular problem:

// grab relationally-shaped results from database
var queryResult = 
(
  from u in myDataContext.Users
  from o in u.Orders
  from op in o.OrderProducts
  select new
  {
    UserName = u.UserName,
    ProductName = op.ProductName,
    Quantity = op.Quantity
  }
).ToList()

// now use a LinqToObjects query to pivot
List<XElement> pivotedResults =
(
  from x in queryResult
  group by x.ProductName into g
  select new XElement(g.Key,
    from y in g
    group by y.UserName into subg
    select new XAttribute(subg.Key, subg.Sum(z => z.Quantity))
).ToList();
David B