可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is is possible to have a local variable in an anonymous c# methods, i.e. in the following code I would like to perform the count only once.
IQueryable<Enquiry> linq = db.Enquiries;
if(...) linq = linq.Where(...);
if(...) linq = linq.Where(e =>
(x <= (from p in db.Orders where p.EnquiryId == e.Id select p).Count() &&
(from p in db.Orders where p.EnquiryId == e.Id select p).Count() <= y));
if(...) linq = linq.Where(...);
var result = (from e in linq select e);
Is there a "let" for anonymous functions?
Update:
Note that I'm adding several Where clauses after this statement so I can't close with a select.
/Niels
回答1:
I've run into a similar problem. The solution is to create a custom expression tree generating method.
I asked my question on MSDN-forums. Please see the question and answer here: Reusing Where expressions.
This may give you an idea on how to proceed, but I must admit that custom expression trees are not for the faint-hearted ;-)
回答2:
Yes, why not?! After all it's a function, just anonymous!
Example:
x => { int y = x + 1; return x + y; }
Or alternatively:
delegate(int x) {
int y = x + 1;
return x + y;
}
So your code can be written as:
... = linq.Where(e => {
var count = (from p in db.Orders where p.EnquiryId == e.Id select p).Count();
return x <= count && count <= y;
});
UPDATE: To clarify things about the comment, it's important to know the difference between anonymous methods and lambda expressions. An anonymous method is just like a normal method, without an explicit name. When you compile it, the compiler generates a normal method with a weird name for you instead, so it will not have any special limitations. However, one representation of an anonymous method is a lambda expression. Lambda expressions can be interpreted in a couple different ways. The first is a delegate. In that way, they are equal to an anonymous method. The second is an expression tree. This way is normally used by LINQ to SQL and some other LINQ providers. They don't execute your expression directly by any means. They parse it as an expression tree and use the tree as input data to generate the equivalent SQL statement to be run on the server. It's not executed like a method and it's not considered an anonymous method. In that case, you can't define a local variable as it's not possible to parse the lambda as an expression tree.
回答3:
Yes, you can do exactly what you want, in Linq to objects and Linq to SQL.
There is a let
in Linq, allowing you to give a name to an intermediate result in the middle of your query, just as you want to. Based on your example:
... = from e in linq
let count = (from p in db.Orders where p.EnquiryId == e.Id select p).Count()
where (x <= count) && (count <= y)
select e;
By the way, I think there was something syntactically erroneous about your original example, which is easier to spot when the count
is just a name:
where (x <= count) && /* <= */ (count <= y);
回答4:
If you're using Linq to SQL, you won't be able to use Mehrdad Afshari's answer. Your LINQ expressions need to be Expression Trees, and those don't support the anonymous delegate syntax.
Neither will you be able to create your delegate elsewhere and call it from inside the lambda - Linq to SQL only allows certain operations to be performed in the body of the query, and calling a delegate isn't one of them.
Your best bet, assuming you're using Linq to SQL (as it appears given your example), is to bring down the count in one query, then capture the count variable in the query that requires the count.
回答5:
The Where method takes a Func so what you're passing in there in the second part ins't actually a method, but just a bool expression. My suggestion would be to have an actual method that returns a bool, that takes in the paremeters you need, and in your call to the Where method you just do something like this Where(p=> MyMethod(p,...))
回答6:
With a little background in Scheme you would know that 'let' is just syntax sugar for defining a lambda and invoking it.
So with that knowledge, lets see how it can be done.
(count => x <= count && count <= y)
((from p in db.Orders
where p.EnquiryId == e.Id
select p).Count())
As a bonus, it looks like Scheme too :)
Disclaimer: I did not test this snippet, but there is no reason it should not work. Personally, I would just use the 'let' construct provided in LINQ.
Update:
It does not work... :(