1

Is it possible to write a linq select with a where clause which can select either ALL items or specific ones? In SQL I could use "where currency like '%'" to return everything. I am trying to write a method so I can pass in the currency (amongst a few other things) and re-use the same method.

e.g.

Just GBP

from a in accounts
where currency.Contains('GBP')
select a

Just GBP

from a in accounts
where currency == 'GBP'
select a

ALL currencies?

from a in accounts
where currency like '%'
select a
BlueChippy
  • 5,935
  • 16
  • 81
  • 131

6 Answers6

7

Have you tried to "store" the query and filter it in a later step, like so:

IEnumerable<AccountClass> GetAccounts(string filter = null)
{
    var query = from a in accounts select a;
    if (!string.IsNullOrEmpty(filter))
    {
        query = query.Where(a => a.Currency.Contains(filter));
    }
    return query;
}

This could be collapsed in a single query, but seems less readable to me and may not work with LINQ-to-SQL (or other LINQ-to-DB where the expression is translated to a query):

from a in accounts
where string.IsNullOrEmpty(filter) || a.Currency.Contains(filter)
select a
Jacek Gorgoń
  • 3,206
  • 1
  • 26
  • 43
Joaquim Rendeiro
  • 1,388
  • 8
  • 13
4

You could try

.Where(c => currencyToFind == null ? true : c==currencyToFind)

And pass in a null for the currency you want if you want them all.

In a linq query expression:

from a in accounts
where (currencyToFind == null ? true : account.Currency==currencyToFind)
select a
Kevek
  • 2,534
  • 5
  • 18
  • 29
  • can you do this in a linq "from...where...select" as well as IEnumerable.Where()? – BlueChippy Sep 04 '11 at 11:54
  • Absolutely, you just need to put that conditional inside the where clause. I will edit my answer to add it. Also, I believe the name for the link type that you're talking about is a "Linq expression". Disclosure: I'm not sitting anywhere near a compiler and I cannot check that this works, but I believe it does. – Kevek Sep 04 '11 at 13:07
  • Thank you - this is the best answer for what I want: User can select a currency code OR select no currency code and then get back a result set both times. – BlueChippy Sep 05 '11 at 04:38
  • 1
    I want to correct myself, this linq type that @BlueChippy was speaking of is not called a linq expression but rather "Linq Query Syntax", or sometimes "Linq Query Expression". The other type (which I alluded to first, with extension methods, is referred to as "Linq Method Syntax". So I don't confuse anyone who comes here later on! – Kevek Sep 06 '11 at 19:59
1

If you want to return all currencies, just don't use any where (both in LINQ and SQL):

from a in accounts
select a

Or, if you really don't need to do anything else, just use accounts.

EDIT: If you want to have one method, and, say, any currency is represented by null, you could do it like this:

IQueryable<Account> result = accounts;

if (currency != null)
    result = result.Where(a => a.Currency == currency);

return result;
svick
  • 236,525
  • 50
  • 385
  • 514
  • This works, but means I have two methods - one for specific currencies and one for all currencies – BlueChippy Sep 04 '11 at 11:05
  • This is the most promising and sensible so far...because what I need my method to do is to return a list of a different concrete class (non EF) avoiding the "parameterless constructor" exception if I try to create in the select directly. – BlueChippy Sep 04 '11 at 11:16
0

Can't you just filter on a condition?

var selectedAccounts = accounts.Select(y => y);
if(!string.IsNullOrEmpty(currency))
{
     selectedAccounts = selectedAccounts.Where(y => y.Currency.Contains(currency));
}

You could even try an even more generic version:

IEnumerable<Account> GetAccounts(Func<Account, bool> filter)
{
    var selectedAccounts = accounts.Select(y => y);
    if(filter != null)
    {
        selectedAccounts = selectedAccounts.Where(filter);
    }
    return selectedAccounts;
}

IEnumerable<Account> GetAccountsForCurrency(string currency)
{
    if(string.IsNullOrEmpty(currency))
    {
         return GetAccounts(null);
    }
    return GetAccounts((y) => y.Currency.Contains(currency));
}

Now you have one more specific and one more general method that could be used for different types of filtering.

Tomas Jansson
  • 22,767
  • 13
  • 83
  • 137
  • This is less readable than putting the condition inside the where with a ?: if statement, I would argue. – Kevek Sep 04 '11 at 11:06
  • Ehh... How is that less readable? I would argue that putting that logic in the query makes the query less readable, so I don't agree with you at all. Also, if you wrap it in a method, as you should, like Joaquin has done in another answer it is much more readable than putting the logic in a where statement. – Tomas Jansson Sep 04 '11 at 11:10
0

It's hard to answer this without more information, but here's the sort of pattern that you appear to want (this would be overengineering in my book unless really warranted):

public static Expression<Func<Account, bool>> GetAccountCurrencyPredicate
   (this FilterKind filter, string value)
{
    switch (filter)
    {
        case FilterKind.Exact:
            return account => account.Currency == value;

        case FilterKind.Contains:
            return account => account.Currency.Contains(value);

        case FilterKind.All:
            return account => true;

        default:
            throw new ArgumentException("Unknown FilterKind.", "filter");
    }
}

And then use it as:

FilterKind filter = ...
string value = ...
IQueryable<Account> accounts = ...

var predicate = filter.GetAccountCurrencyPredicate(value);
var matchingAccounts = accounts.Where(predicate); 
Ani
  • 111,048
  • 26
  • 262
  • 307
-1

Why are you using a where statement if you want to return anything? where is made for filtering, so if you want to return anything you can either try

from a in accounts
select a

or, if you want to have a where statement in it

from a in accounts
where 1 = 1
select a

or, if you want a like statement i suggest using a regex

from a in accounts
let r = new Regex("expression")
where r.IsMatch(a.currency)
select a;
skeleten
  • 94
  • 1
  • 10
  • He wants to use a where statement to filter sometimes. This is perfectly valid and if simple can be done with a if statement (as in my answer), or by passing a conditional Func to the .Where() – Kevek Sep 04 '11 at 11:04
  • `where 1 = 1` won't compile, `where 1 == 1` or `where true` would, but it's pointless. – svick Sep 04 '11 at 11:13
  • 1
    @Korayem, this is not LINQ to objects, so using regex won't work. – svick Sep 04 '11 at 11:14