开发者

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?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜