简体   繁体   中英

Generic LINQ to SQL Extension Methods on collection of various derived types

I'm trying to create a generic extension method for an IQueryable<T> . T is an abstract Player and the IQueryable can contain concrete types Goalkeeper and Striker .

public abstract class Player
{
    public string Name { get; set; }
    public string Fouls { get; set; }
}

public class Goalkeeper : Player
{
    public int Saves { get; set; }

}

public class Striker : Player
{
    public int Goals { get; set; }
}

The extension methods that are working (the simple ones) look like this:

public static IQueryable<Goalkeeper> NotPerforming(this IQueryable<Goalkeeper> goalkeepers)
{
    return goalkeepers.Where(g => g.Saves < goalkeepers.Average(x => x.Saves));
}

public static IQueryable<Striker> NotPerforming(this IQueryable<Striker> strikers)
{
    return strikers.Where(g => g.Goals < strikers.Average(x => x.Goals));
}

Which I can use like this:

var badGoalies = players.OfType<Goalkeeper>().NotPerforming();
var badStrikers = players.OfType<Striker>().NotPerforming();

So now I want to query for all players who are not performing well.

var badPlayers = players.NotPerforming();

Which I can't seem to get to work properly.

public static IQueryable<T> NotPerforming<T>(this IQueryable<T> players)
    where T : Player
{
    // what to do here?
}

I tried things like...

return players.OfType<Striker>().NotPerforming()
          .Union(
              players.OfType<Goalkeeper>().NotPerforming()
          );

Which doesn't work.

What is the best practice way of doing this -- and without leaving LINQ-to-SQL because I'd like to keep chaining extension methods -- and keeping good performance in mind?

You can create another extension method and put there a logic:

public static IQueryable<Player> NotPerforming(this IQueryable<Player> players)
{
    var notPerformingGoalKeepers = players.NotPerformingGoalkeepers();
    var notPerformingStrikers = players.NotPerformingStrikers();

    return notPerformingGoalKeepers.Cast<Player>()
        .Concat(notPerformingStrikers);
}

public static IQueryable<Goalkeeper> NotPerformingGoalkeepers(this IQueryable<Player> players)
{
    var goalkeepers = players.OfType<Goalkeeper>();
    return goalkeepers.Where(g => g.Saves < goalkeepers.Average(x => x.Saves));
}

public static IQueryable<Striker> NotPerformingStrikers(this IQueryable<Player> players)
{
    var strikers = players.OfType<Striker>();
    return strikers.Where(g => g.Goals < strikers.Average(x => x.Goals));
}

And use:

var badPlayers = players.NotPerforming();

Or another approach:

public static IQueryable<Player> NotPerforming<T>(this IQueryable<Player> players) where T : Player
{
    if (typeof(T) == typeof(Goalkeeper))
    {
        return players.OfType<Goalkeeper>().NotPerforming();
    }

    if (typeof(T) == typeof(Striker))
    {
        return players.OfType<Striker>().NotPerforming();
    }

    return null;
}

private static IQueryable<Goalkeeper> NotPerforming(this IQueryable<Goalkeeper> goalkeepers)
{
    return goalkeepers.Where(g => g.Saves < goalkeepers.Average(x => x.Saves));
}

private static IQueryable<Striker> NotPerforming(this IQueryable<Striker> strikers)
{
    return strikers.Where(g => g.Goals < strikers.Average(x => x.Goals));
}

And use:

var badStrikers = players.NotPerforming<Striker>();
var badGoalkeepers = players.NotPerforming<Goalkeeper>();

var badPlayers = players.NotPerforming<Striker>()
    .Concat(players.NotPerforming<Goalkeeper>());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM