Claudio 的个人资料Claudio Lassala in Softw...照片日志列表更多 工具 帮助
4月21日

Putting LINQ queries together, one piece at a time

Last week, good pal Rod Paddock asked me an interesting question about LINQ, and I think this may be useful to others (not that the answer may not already be out there somewhere, of course...).

The main scenario was that different queries had to be run depending on some options. The from and select parts of the query where pretty much the same; the parts that varied were the where and orderby ones. Here's some code to illustrate a simplified scenario:

 

Collection<string> months = new Collection<string> { "January", "February", 
    "March", "April", "May", "June", "July", "August", "September", "October", 
    "November", "December" };

IEnumerable<string> query = null;

int? minNumberOfLetters = 5;

if (minNumberOfLetters.HasValue)
{
    query = from m in months
            where m.Length >= minNumberOfLetters
            select m;
}
else
{
    query = from m in months
            where m.Length >= minNumberOfLetters
            select m;
}

bool shouldOrderByLength = true;

if (shouldOrderByLength)
{
    query = from m in months
            where m.Length >= minNumberOfLetters
            orderby m.Length
            select m;
}

foreach (var item in query)
{
    Console.WriteLine(item);
}

The code should query only months whose name length is equals to or greater than minNumberOfLetter (in the sample above, that'd be 5), unless that variable holds a null, in which case all months should be returned. Also, the query should be ordered by the length of months names, in case shouldOrderByLength is true (obvioulsy, both minNumberOfLetter and shouldOrderByLenght wouldn't be hard-coded, but instead, read from user input or whatever...).

The main problem with such code is that there's a lot of redundancy in the queries. What if, for instance, instead of doing select m, we wanted to do select m.ToUpper()? We'd need to make that change in multiple places.

Queries can be broken up into smaller pieces. Queries don't get executed until they're iterated over, either by using in a foreach block, or calling ToList() on it (which uses a foreach internally...). That means we can tweak with them until right before the iteration happens.

So, we can refactor the previous code where we first declare the part that doesn't vary in the query:

var query = from m in months select m;

Remember, the query above does NOT get executed just yet; we are just defining it, but not executing it (think of it as building a SQL query by concatenating strings, and eventually executing the query against the database).

Then we can add the where clause to the query if appropriate:

if (minNumberOfLetters.HasValue)
{
    query = query.Where((string m) => m.Length >= minNumberOfLetters);
}

Same with the orderby clause:

if (shouldOrderByLength)
{
    query = query.OrderBy((string m) => m.Length);
}

Whenever we're done building the query, then we can iterate over it as needed.

Another cool thing we can do: say we need to order the query according to some specific logic; for example, if some variable is true we want to order the query by the month's name, otherwise we want .to order it by the name's length. That could be done something like this:

 

Func<string, object> orderBy = null;

if (orderByName)
{
    orderBy = (string m) => m;
}
else
{
    orderBy = (string m) => m.Length;
}

query = query.OrderBy(orderBy);

Basically, we declare a Func<string, object> variable that is going to hold the expression for ordering. In the if-block we assign a lambda expression to the variable accordingly. Finally, we add the call to OrderBy to the query, passing in the expression.

Here's the complete final code, in case you want to mess with it:

 

Collection<string> months = new Collection<string> { "January", "February", "March", 
"April", "May", "June", "July", "August", "September", "October",
"November", "December" }; var query = from m in months select m; int? minNumberOfLetters = 5; if (minNumberOfLetters.HasValue) { query = query.Where((string m) => m.Length >= minNumberOfLetters); } bool shouldOrderByLength = true; if (shouldOrderByLength) { query = query.OrderBy((string m) => m.Length); } bool orderByName = true; Func<string, object> orderBy = null; if (orderByName) { orderBy = (string m) => m; } else { orderBy = (string m) => m.Length; } if (orderBy != null) { query = query.OrderBy(orderBy); } foreach (var item in query) { Console.WriteLine(item); }

评论 (2)

请稍候...
很抱歉,您输入的评论太长。请缩短您的评论。
您没有输入任何内容,请重试。
很抱歉,我们当前无法添加您的评论。请稍后重试。
若要添加评论,需要您的家长授予您相应权限。请求权限
您的家长禁用了评论功能。
很抱歉,我们当前无法删除您的评论。请稍后重试。
您已超过了一天之内允许提供的评论数上限。请在 24 小时后重试。
因为我们的系统表明您可能在向其他用户提供垃圾评论,您的帐户已禁用了评论功能。如果您认为我们错误地禁用了您的帐户,请联系 Windows Live 支持部门
完成下面的安全检查,您提供评论的过程才能完成。
您在安全检查中键入的字符必须与图片或音频中的字符一致。

若要添加评论,请使用您的 Windows Live ID 登录(如果您使用过 Hotmail、Messenger 或 Xbox LIVE,您就拥有 Windows Live ID)。登录


还没有 Windows Live ID 吗?请注册

没有名字发表:
lianyk Welcome to wow gold our wow Gold and wow power leveling store. We wow gold are specilized, wow power leveling professional and reliable wow power leveling website for wow power leveling selling and wow gold service. By the World of Warcraft gold same token,we offer wow power leveling the best WoW service wow power leveling for our long-term and wow powerleveling loyal customers. wow powerleveling You will find wow powerleveling the benefits and value powerleveling we created powerleveling different from other sites. As to most people, power leveling they are unwilling to power leveling spend most of wow power leveling the time wow gold grinding money Rolex for mounts or rolex replica repair when replica rolex they can purchase Watches Rolex what they Rolex Watches are badly need. The Watch Rolex only way is to look Rolex Watch for the best place rs gold to buy cheap WOW gold. Yes! You find it here! Our WoW Gold supplying service has already accumulated a high reputation and credibility. We have plenty of Gold suppliers, which will guarantee our delivery instant. Actually, we have been getting Runescape Gold tons of postive feedbacks from our loyal RuneScape Money customers who really appreciate our service.
11 月 12 日
Claudio,
 
Nice example, but unless I'm missing something I think you mean "List" instead of "Collection" in the example source code. Unless Collection is a new keyword?
 
Instead of Collection<string>
shouldn't it be...
List<string>
?
5 月 13 日

引用通告

此日志的引用通告 URL 是:
http://claudiolassala.spaces.live.com/blog/cns!E2A4B22308B39CD2!705.trak
引用此项的网络日志