views:

76

answers:

1

I need to implement 3 public methods that all rely on the same initial join. Would a solution like the one described below work?

The code below is an example, the real stuff is much more complicated, that's why I want to reuse it.

private Tuple<Customer,Order> GetCustomerOrders()
{
    from c in customers
    join o in orders
        on c.customerid equals o.customerid
    select Tuple.Create(c, o)
}

public MyCustomerOrder GetCustomerOrder(int customerId)
{
    return (from co in GetCustomerOrders()
    where co.Item1.customerid == customerId
    select new MyCustomerOrder(co.Item1, co.Item2)).FirstOrDefault();
}

public IEnumerable<MyCustomerOrder> GetCustomerOrders()
{
    return from co in GetCustomerOrders()
    orderby co.Item1.Name
    select new MyCustomerOrder(co.Item1, co.Item2);
}

The question is, does the tuple break the query? In other words, will this end up in the SQL query that gets generated where co.Item1.customerid == customerId?

+1  A: 

It really depends on whether LINQ to SQL understands the point of Tuple.Create. I suspect that it does in .NET 4.0 - but the only way to find out is to try it.

It certainly makes conceptual sense, and composability is part of the goal of LINQ - which is why I'd hope it's supported. Effectively it's just like using an anonymous type, except you get to "export" the type information out of the method, which is the point of Tuple.

Jon Skeet
Unfortunately it doesn't seem to work though: "LINQ to Entities does not recognize the method 'System.Tuple..."
Sander Rijken
@Sander: That's sad :( Maybe it's worth creating a Connect issue?
Jon Skeet
L2E won't recognize *any* constructor with parameters. You need to find a solution which uses parameterless constructors. (Initializers are OK, though.)
Craig Stuntz
@Craig: Does that include anonymous types? That would be very odd. (Anonymous types generated in C# don't have parameterless constructors.) Note that Tuple.Create isn't a constructor call - it's a static method. I was really hoping that L2E would know about that :(
Jon Skeet
Jon, I'm not sure what you mean. **From the C# viewpoint**, `new { Foo = "bar" }` isn't a parameterized constructor; it's a parameterless constructor with an initializer. And it works fine in L2E. Likewise, `new Baz("bar")` is *not* allowed in L2E, but `new Baz { Foo = "bar" } *is.* (Why? I don't know.) Regarding static method calls, L2E won't accept *any* method which isn't mapped, either built in or via EDMX. See http://msdn.microsoft.com/en-us/library/bb738681%28VS.100%29.aspx for built-in mappings.
Craig Stuntz
@Craig: No, it's not a parameterless constructor call. It looks a bit like it, but that's not the case. It's an anonymous-object-creation-expression (section 7.5.10.6) instead of an object-creation-expression (section 7.5.10.1). The properties of an anonymous type (in C#) are *read-only*. If you look at section 7.5.10.6 you'll see the *parameterized* constructor which is created in the anonymous type. My point about Tuple is that those methods *should* be mapped by L2E itself.
Jon Skeet
Jon, I may be calling it by the wrong words, but: (1) L2E *supports* anonymous type creation, using the initializer syntax. (2) It *supports* parameterless constructors on named types. (3) It *does not* support parameterized constructors on those types. (4) It *does support* initializers on those types. I agree with you about `Tuple.Create`; it should be supported. I'll open a connect report if I can't find one already, presuming that the docs are actually correct (I haven't tested this yet).
Craig Stuntz
Right, I agree with all of those points - although I'm interested in how L2E detects that a type is anonymous. I wonder how hard one would have to try in order to pull the wool over its eyes ;) And yes, a Connect report would be welcome, IMO.
Jon Skeet
I tested this in VS 2010, and, indeed, it does not work. So: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=520269
Craig Stuntz