简体   繁体   中英

Combining two extension methods into one

I have this extension method:

public static IQueryable<T> FilterByEmployee<T>(this IQueryable<T> source, EmployeeFilter filter) 
    where T : class, IFilterableByEmployee
    {
        if (!string.IsNullOrEmpty(filter.Gender))
            source = source.Where(e => e.Employee.Gender == filter.Gender);

        if (!string.IsNullOrEmpty(filter.NationalityID))
            source = source.Where(e => e.Employee.NationalityID == filter.NationalityID);

        // filter the group
        if (filter.IncludeChildGroups)
        {
            var groups = Security.GetAllChildGroups(filter.GroupID);
            source = source.Where(e => e.Employee.EmployeeGroupID.HasValue
                && groups.Contains(e.Employee.EmployeeGroupID.Value));
        }
        else
        {
            source = source.Where(e => e.Employee.EmployeeGroupID == filter.GroupID);
        }

        // filter status
        if (filter.OnlyActiveEmployees)
            source = source.Where(e => e.Employee.Status == "Active");

        return source;
    }

and another one, which is exactly the same, but it filters the Employees context directly:

public static IQueryable<T> Filter<T>(this IQueryable<T> source, EmployeeFilter filter) 
    where T : Employee
    {
        if (!string.IsNullOrEmpty(filter.Gender))
            source = source.Where(e => e.Gender == filter.Gender);

        if (!string.IsNullOrEmpty(filter.NationalityID))
            source = source.Where(e => e.NationalityID == filter.NationalityID);

        // filter the group
        if (filter.IncludeChildGroups)
        {
            var groups = Security.GetAllChildGroups(filter.GroupID);
            source = source.Where(e => e.EmployeeGroupID.HasValue
                && groups.Contains(e.EmployeeGroupID.Value));
        }
        else
        {
            source = source.Where(e => e.EmployeeGroupID == filter.GroupID);
        }

        // filter status
        if (filter.OnlyActiveEmployees)
            source = source.Where(e => e.Status == "Active");

        return source;
    }

I hate the idea of having almost the same code twice, how can I combine these two methods into one? (if possible) or at least make it two methods but the filtering in one of them? the original code is much longer that's also one of the reasons.

You could implement IFilterByEmployee on Employee directly and explicitly:

public class Employee : IFilterByEmployee
{
    Employee IFilterByEmployee.Employee
    {
        get { return this; }
    }
}

By implementing the interface explicitly, it essentially makes this a 'means to an end' kind of solution.

EDIT: This would likely not work with LinqToEf. You have the same problem with writing the SQL directly. The context of the query is critical here thus making it very difficult to abstract it in a way that LinqToEf could intelligently (or magically) interpret it correctly.

This should be possible with LINQKit :

public static IQueryable<T> Filter<T>(this IQueryable<T> source, Expression<Func<T, Employee>> employeeSelector, EmployeeFilter filter)
{
    source = source.AsExpandable();

    if (!string.IsNullOrEmpty(filter.Gender))
        source = source.Where(e => employeeSelector.Compile().Invoke(e).Gender == filter.Gender);

    if (!string.IsNullOrEmpty(filter.NationalityID))
        source = source.Where(e => employeeSelector.Compile().Invoke(e).NationalityID == filter.NationalityID);

    // filter the group
    if (filter.IncludeChildGroups)
    {
        var groups = Security.GetAllChildGroups(filter.GroupID);
        source = source.Where(e => employeeSelector.Compile().Invoke(e).EmployeeGroupID.HasValue
            && groups.Contains(employeeSelector.Compile().Invoke(e).EmployeeGroupID.Value));
    }
    else
    {
        source = source.Where(e => employeeSelector.Compile().Invoke(e).EmployeeGroupID == filter.GroupID);
    }

    // filter status
    if (filter.OnlyActiveEmployees)
        source = source.Where(e => employeeSelector.Compile().Invoke(e).Status == "Active");

    return source;
}

source = source.AsExpandable(); creates a wrapper around the EF query that ensures that employeeSelector.Compile().Invoke(e) gets translated appropriately and does not, despite how it looks, actually compile any expression tree, and EF should only see expressions that it actually supports.

You can use e => e as the employee selector if you filter directly on the employees, or e => e.Employee if you don't.

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