Can I use Func/Predicate or Linq expression to create generic filtering of a List<T>?
With fo开发者_如何学Cllowing class model
public class Client
{
public string Name;
public List<Projects> Projects;
}
public class Projects
{
public string Value;
}
I want to create few methods: GetHighGoldCustomers(); GetHighSilverCustomers(); GetHighBronzeCustomers()
.... etc
We have around 15 different ratings.
The rating of the client is dependent on the value of the projects so for GetHighGoldCustomers(), I want to get all clients and those projects which have value > 200
To explain further:
I want List<Client>
back. Each client will have List<Project>
and those projects will be subset of the projects of the client.
Can I achieve this by creating a generic method in which I pass the Expression as a parameter that then returns a filtered out List<Client> and related List<Project>
?
Thank you
Yes, you can pass the Expression as a parameter ..like this ↓
public static IEnumerable<T> GetResultByCondtion<T>(
IQueryable<T> src,
Expression<Func<T, bool>> predicate)
{
return src.Where(predicate);
}
Usage:
var result = GetResultByCondtion<Client>(Clients.AsQueryable() ,
c=>c.Projects.Sum(p=>p.Value) > 20);
Console.WriteLine(result.Count());
Console.ReadKey();
You mean something like this?
private List<Client> ClientsWithValueGreaterThan(int value)
{
return Clients.Where(c => c.Projects.Sum(p => p.Value) > value)
}
Then, based on this one, you can create the methods you describe:
public GetHighGoldCustomers()
{
return ClientsWithValueGreaterThan(200);
}
public GetHighSilverCustomers()
{
return ClientsWithValueGreaterThan(100);
}
I'd look at creating a set of extension methods to solve your problem. Something like this:
public static class ClientEx
{
public static List<Client> GetCustomersByProjects(
this IEnumerable<Client> clients,
Func<IEnumerable<Projects>, IEnumerable<Projects>> selector)
{
var query =
from c in clients
let ps = selector(c.Projects).ToList()
where ps.Any()
select new Client()
{
Name = c.Name,
Projects = ps,
};
return query.ToList();
}
public static List<Client> GetCustomersByProjectValuesGreaterThan(
this IEnumerable<Client> clients, int value)
{
return clients.GetCustomersByProjects(ps => ps.Where(p => p.Value > value));
}
public static List<Client> GetHighGoldCustomers(
this IEnumerable<Client> clients)
{
return clients.GetCustomersByProjectValuesGreaterThan(200);
}
}
Then you can use these methods on any IEnumerable<Client>
like so:
var customers = new List<Client>();
// load customers
var highGold = customers.GetHighGoldCustomers();
var moreThan100 = customers.GetCustomersByProjectValuesGreaterThan(100);
Now, if I can go out on a limb, I would suggest creating a method to return customers by any of the 15 ratings you mentioned in your question.
First, create an enum to represent the ratings:
public enum ClientRating
{
HighGold,
HighSilver,
HighBronze,
Gold,
Silver,
Bronze,
LowGold,
LowSilver,
LowBronze,
// etc
}
Then, add the following code to the extension class:
private static Dictionary<
ClientRating, Func<IEnumerable<Projects>, IEnumerable<Projects>>>
_ratingSelectors = new Dictionary<
ClientRating, Func<IEnumerable<Projects>, IEnumerable<Projects>>>()
{
{ ClientRating.HighGold, ps => ps.Where(p => p.Value > 200) },
{ ClientRating.HighSilver, ps => ps.Where(p => p.Value > 125) },
{ ClientRating.HighBronze, ps => ps.Where(p => p.Value > 60) },
{ ClientRating.Gold, ps => ps.Where(p => p.Value > 175) },
{ ClientRating.Silver, ps => ps.Where(p => p.Value > 100) },
{ ClientRating.Bronze, ps => ps.Where(p => p.Value > 40) },
{ ClientRating.LowGold, ps => ps.Where(p => p.Value > 150) },
{ ClientRating.LowSilver, ps => ps.Where(p => p.Value > 80) },
{ ClientRating.LowBronze, ps => ps.Where(p => p.Value > 20) },
};
public static List<Client> GetCustomersByRating(
this IEnumerable<Client> clients,
ClientRating rating)
{
return clients.GetCustomersByProjects(_ratingSelectors[rating]).ToList();
}
Then you would be able to query your clients like so:
var highGolds = customers.GetCustomersByRating(ClientRating.HighGold);
var silvers = customers.GetCustomersByRating(ClientRating.Silver);
var lowBronzes = customers.GetCustomersByRating(ClientRating.LowBronze);
The nice thing about this code is that the definitions of the ratings are all kept in one spot in your code and you can also make modifications to the dictionary at run-time if you need to change the rating system without recompiling.
I suspect that you'd need to change the selector
expression if the rating system relies on properties on the Client
rather than just the list of projects and if you need to, for example, filter out HighGold
clients when returning HighSilver
.
Does this help?
精彩评论