The fact that I expected this to work and it didn't leads me to search for the piece of the picture that I don't see.
3 Answers
Imagine that query being remoted over to a database. How is the database engine supposed to reach over the internet as it is executing the query and tell the "count" variable on your machine to update itself? There is no standard mechanism for doing so, and therefore anything that would mutate a variable on the local machine cannot be put into an expression tree that would run on a remote machine.
More generally, queries that cause side effects when they are executed are very, very bad queries. Never, ever put a side effect in a query, even in the cases where doing so is legal. It can be very confusing. Remember, a query is not a for
loop. A query results in an object represents the query, not the results of the query. A query which has side effects when executed will execute those side effects twice when asked for its results twice, and execute them zero times when asked for the results zero times. A query where the first part mutates a variable will mutate that variable before the second clause executes, not during the execution of the second clause. As a result, many query clauses give totally bizarre results when they depend on side effect execution.
For more thoughts on this, see Bill Wagner's MSDN article on the subject:
http://msdn.microsoft.com/en-us/vcsharp/hh264182
If you were writing a LINQ-to-Objects query you could do what you want by zipping:
var zipped = model.DirectTrackCampaigns.Top("10").Zip(Enumerable.Range(0, 10000), (first, second)=>new { first, second });
var res = zipped.Select(c=> new Campaign { IsSelected = c.second % 2 == 0, Name = c.first.CampaignName };
That is, make a sequence of pairs of numbers and campaigns, then manipulate the pairs.
How you'd do that in LINQ-to-whatever-you're-using, I don't know.

- 647,829
- 179
- 1,238
- 2,067
-
in theory if count was a MarshalByRefObject could the compiler let it slide and then have the runtime expect valid remoting config? – Aaron Anodide Oct 05 '11 at 20:40
-
1@Gabriel: The question remains: *what are you going to tell SQL Server*? Somehow that query has to be turned into SQL and remoted over to the database. What SQL do you propose "count++" be turned into? – Eric Lippert Oct 05 '11 at 20:47
-
2You can probably get away without the call to Zip because there's an overload to the [`Select`](http://msdn.microsoft.com/en-us/library/bb534869.aspx) method that passes the index into the selector function. – Roman Oct 05 '11 at 20:49
-
can i call that using the query syntax? – Aaron Anodide Oct 05 '11 at 20:52
-
@Gabriel: There is no query syntax for select-with-index, unfortunately. Nor is there a query syntax for zip joins -- we considered adding one, but decided against it. – Eric Lippert Oct 05 '11 at 20:56
-
2I agree that expression trees with assignment would be horrible when trying to send them to SQL Server. But expression trees are useful for other things as well! Why are all expression trees limited by the language, instead of Linq to SQL failing with an "assignment is illegal" exception? – configurator Oct 05 '11 at 21:21
-
@configurator: Expression trees were designed with LINQ-to-SQL / LINQ-to-Entites / etc in mind. Anything that didn't further that aim was cut. The C# team was the "long pole" of that release -- every day we missed a deadline, the product slipped. I happened to be the developer with the most work on my schedule on the C# team, and so every day I was late, the product slipped. (No pressure!) That work included expression tree translation so we aggressively cut everything we could to make sure we came in on time and under budget. Fortunately we have a strong team and managed no problem. – Eric Lippert Oct 05 '11 at 21:26
-
I personally would love to make C# more homoiconic and have full support for all expressions (and statements, and declarations!) in expression trees. Those features have never made it to the top of the priority queue; compared to dynamic in C# 4 or async in the next release, filling in the corners of expression trees is not a compelling way to spend our budget. I hope to get that in there someday, but I would not hold my breath waiting if I were you. – Eric Lippert Oct 05 '11 at 21:28
-
@EricLippert: Makes sense. Since your compiler doesn't actually use expression trees to compile, it's not like it creates the expression trees and then disallows them. If you had written the C# 4 compiler in C# 4 however, it would have been easier for you to just leave that in :) – configurator Oct 05 '11 at 21:29
When you pass anything to a method that expects an Expression, nothing is actually evaluated at that time. All it does is, it breaks apart the code and creates an Expression tree out of it.
Now, in .Net 4.0, alot of things were added to the Expression API (including Expression.Increment and Expression.Assign which would actually be what you're doing), however the compiler (I think it's a limitation of the C# compiler) hasn't been updated yet to take advantage of the new 4.0 Expression stuff. Therefore, we're limited to method calls, and assignment calls will not work. Hypothetically, this could be supported in the future.

- 102,548
- 21
- 159
- 201
-
My guess is that limitation is very deliberate and it won't be supported in the future. Those new expression types were added because of DLR, which uses them internally. But they are not meant for use in querying, which is the primary (but certainly not the only one) use of C#'s lambda to expression tree translation. – svick Oct 05 '11 at 20:15
-
@svick And it seems that Mr. Linq-to-everything (Bart De Smet) would agree with you: "What hasn’t changed though, at the moment, is the language support for expression tree generation. That is, when writing a statement bodied lambda expression and assigning it to an Expression
variable, the compiler won’t turn it into an expression tree using the new nodes for you. It keeps on supporting the subset required for LINQ. Till we truly enter the domain of meta-programming, I except the language to stay with the true expression tree subset." http://bit.ly/PvcAr – BFree Oct 05 '11 at 20:18
IsSelected = (count++) % 2 == 0
Pretty much means:
IsSelected = (count = count + 1) % 2 == 0
That's where the assignment is happening and the source of the error.
According to the different answers to this question, this should be possible with .NET 4.0 using expressions, though not in lambdas.
-
Well, it's possible with .Net 4.0 `Expression`s, but it's not possible with C# lambdas, even in version 4.0 of the language. – svick Oct 05 '11 at 20:11
-
1`count++` certainly does not translate to `count = count + 1`. A post-increment operator is its own operator and does not get translated into any other operator. – James Dunne Oct 05 '11 at 22:40