patterncsharpMinor
Applying filters to a table
Viewed 0 times
tableapplyingfilters
Problem
I've written a lengthy procedure that I call a few times to apply filters defined by the customer to a table, as I didn't see how I could turn my column name string into a LINQ column and how I could turn my action string into an action on a string in such way that the function can be used in the LINQ query like:
I wrote this instead as a temporary solution:
```
private void applyFilterAction(ref IQueryable products, FilterAction action)
{
var actionValue = action.Value.ToLower();
var column = action.Column;
if (action.Action.Equals("StartsWith"))
{
if (column.Equals("Description")) products = from p in products where p.Description.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("LongDescription")) products = from p in products where p.LongDescription.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Provider")) products = from p in products where p.Provider.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("ProviderCode")) products = from p in products where p.ProviderCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Publisher")) products = from p in products where p.Publisher.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("PublisherCode")) products = from p in products where p.PublisherCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Custom1")) products = from p in products where p.Custom1.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Cus
private void applyFilterAction(ref IQueryable products, FilterAction action)
{
products = from p in products where MAGIC(p, action.Column).ToLower().MAGIC2(action.Action, action.Value) == action.AddOrKill select p;
}I wrote this instead as a temporary solution:
```
private void applyFilterAction(ref IQueryable products, FilterAction action)
{
var actionValue = action.Value.ToLower();
var column = action.Column;
if (action.Action.Equals("StartsWith"))
{
if (column.Equals("Description")) products = from p in products where p.Description.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("LongDescription")) products = from p in products where p.LongDescription.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Provider")) products = from p in products where p.Provider.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("ProviderCode")) products = from p in products where p.ProviderCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Publisher")) products = from p in products where p.Publisher.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("PublisherCode")) products = from p in products where p.PublisherCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Custom1")) products = from p in products where p.Custom1.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
else if (column.Equals("Cus
Solution
-
YOU HAVE A LOT OF CODE REPEATING ITSELF - it definitely means something. Basically you have two fields to consider -
Here is a sample:
This approach is much better but I would go even further. Instead of this
It has nothing to do with LINQ yet, only common sense. This was the main part. My other points are:
-
UpperCamelCase?
-
-
I would not use such a method signature - void with ref parameter. It doesn't look solid with existing LINQ methods and there is no point in having such a signature. I would use regular LINQ
-
I believe in such cases you should create fluent interface - it will improve code readability a lot. Especially in implementing
YOU HAVE A LOT OF CODE REPEATING ITSELF - it definitely means something. Basically you have two fields to consider -
action and column. Since all your columns are considered to be strings so you can separate a) logic which determines which column to take and b) logic which determines how to filter columns. This will lead to dramatical changes in your code.Here is a sample:
private void applyFilterAction(ref IQueryable products, FilterAction action)
{
var actionValue = action.Value.ToLower();
var column = action.Column;
Func valueExtractor;
// determining field
if (column.Equals("Description")) valueExtractor = p => p.Description;
else if (column.Equals("LongDescription")) valueExtractor = p => p.LongDescription;
else if (column.Equals("Provider")) valueExtractor = p => p.Provider;
else if (column.Equals("ProviderCode")) valueExtractor = p => p.ProviderCode;
else if (column.Equals("Publisher")) valueExtractor = p => p.Publisher;
else if (column.Equals("PublisherCode")) valueExtractor = p => p.PublisherCode;
else if (column.Equals("Custom1")) valueExtractor = p => p.Custom1;
else if (column.Equals("Custom2")) valueExtractor = p => p.Custom2;
else if (column.Equals("Custom3")) valueExtractor = p => p.Custom3;
else if (column.Equals("EanCode")) valueExtractor = p => p.EanCode;
else throw new NotSupportedException(column);
Predicate filteringPredicate;
// determining predicate
if (action.Action.Equals("StartsWith")) filteringPredicate = s => s.StartsWith(actionValue);
else if (action.Action.Equals("EndsWith")) filteringPredicate = s => s.EndsWith(actionValue);
else if (action.Action.Equals("Contains")) filteringPredicate = s => s.Contains(actionValue);
else if (action.Action.Equals("Exact")) filteringPredicate = s => s.Equals(actionValue);
else throw new NotSupportedException(action.Action);
products = from p in products where filteringPredicate(valueExtractor(p).ToLower()) == action.AddOrKill select p;
}This approach is much better but I would go even further. Instead of this
if blocks I would use dictionaries which will map input string into corresponding delegates .It has nothing to do with LINQ yet, only common sense. This was the main part. My other points are:
-
UpperCamelCase?
-
action.AddOrKill looks very strange. It took me a while to understand what are you trying to achieve with it. It looks very weird and should be rewritten in some other, more developer-friendly way. -
I would not use such a method signature - void with ref parameter. It doesn't look solid with existing LINQ methods and there is no point in having such a signature. I would use regular LINQ
IQueryable MethodName(IQueryable<> source, ...other parameters). -
I believe in such cases you should create fluent interface - it will improve code readability a lot. Especially in implementing
NOT functionality.Code Snippets
private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
{
var actionValue = action.Value.ToLower();
var column = action.Column;
Func<TempArticle, string> valueExtractor;
// determining field
if (column.Equals("Description")) valueExtractor = p => p.Description;
else if (column.Equals("LongDescription")) valueExtractor = p => p.LongDescription;
else if (column.Equals("Provider")) valueExtractor = p => p.Provider;
else if (column.Equals("ProviderCode")) valueExtractor = p => p.ProviderCode;
else if (column.Equals("Publisher")) valueExtractor = p => p.Publisher;
else if (column.Equals("PublisherCode")) valueExtractor = p => p.PublisherCode;
else if (column.Equals("Custom1")) valueExtractor = p => p.Custom1;
else if (column.Equals("Custom2")) valueExtractor = p => p.Custom2;
else if (column.Equals("Custom3")) valueExtractor = p => p.Custom3;
else if (column.Equals("EanCode")) valueExtractor = p => p.EanCode;
else throw new NotSupportedException(column);
Predicate<string> filteringPredicate;
// determining predicate
if (action.Action.Equals("StartsWith")) filteringPredicate = s => s.StartsWith(actionValue);
else if (action.Action.Equals("EndsWith")) filteringPredicate = s => s.EndsWith(actionValue);
else if (action.Action.Equals("Contains")) filteringPredicate = s => s.Contains(actionValue);
else if (action.Action.Equals("Exact")) filteringPredicate = s => s.Equals(actionValue);
else throw new NotSupportedException(action.Action);
products = from p in products where filteringPredicate(valueExtractor(p).ToLower()) == action.AddOrKill select p;
}Context
StackExchange Code Review Q#744, answer score: 7
Revisions (0)
No revisions yet.