简体   繁体   中英

Make Linq function more generic parameters

How do I generalize this function? I want Name property to be variable, and have function accept, replace the persons class with any class.

// Filtering logic  
Func<SampleFilterModel, IEnumerable<Person>> filterData = (filterModel) =>  
{  
    return persons.Where(p => p.Name.StartsWith(filterModel.Term ?? String.Empty, StringComparison.InvariantCultureIgnoreCase))  
       .Skip((filterModel.Page-1) * filter.Limit)  
       .Take(filterModel.Limit);  
};  

Other items:

IEnumerable<Person> persons = new List<Person>() {
    new Person() { Name = "Laura Callahan", DOB = DateTime.Parse("1958-01-09"), Email = "laura.callahan@test.com" },
    new Person() { Name = "Anne Dodsworth", DOB = DateTime.Parse("1966-01-27"), Email = "anne.dodsworth@test.com" }
};

public class SampleFilterModel
{
    public int Page { get; set; }
    public int Limit { get; set; }
    public string Term { get; set; }

    public SampleFilterModel()
    {
        this.Page = 1;
        this.Limit = 3;
    }

    public object Clone()
    {
        var jsonString = JsonConvert.SerializeObject(this);
        return JsonConvert.DeserializeObject(jsonString, this.GetType());
    }
}

Current attempt, trying to modify/rework as needed:

giving some problems/errors:

  1. Do I have to declare all variables as static?

  2. Getting error message:

    'T' does not contain a definition for 'propertyInfo' and no accessible extension method 'propertyInfo' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)

Code:

public class Filterclass<T> where T : class
{
    public static string ColumnName;
    public static SampleFilterModel filter = new SampleFilterModel();
    public static IEnumerable<T> input;

    public Func<SampleFilterModel, IEnumerable<T>> filterData = (filterModel) =>
    {
        var propertyInfo = input.GetType().GetProperty(ColumnName);

        return input.Where(p => p.propertyInfo.StartsWith(filterModel.Term ?? String.Empty, StringComparison.InvariantCultureIgnoreCase))
        .Skip((filterModel.Page - 1) * filter.Limit)
        .Take(filterModel.Limit);
    };
}

While the rest of the question does not make much sense to me, the part about trying to make a generic function would require building up a predicate for the Where call

Review the comments included to get an example of how to build up the lambda expression used for the predicate

public static class Filterclass {
    static readonly MethodInfo startsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string), typeof(System.StringComparison) });

    public static IEnumerable<T> FilterData<T>(this IEnumerable<T> input, string columnName, FilterModel filterModel) where T : class {
        var type = typeof(T);
        var propertyInfo = type.GetProperty(columnName);
        //T p =>
        var parameter = Expression.Parameter(type, "p");
        //T p => p.ColumnName
        var name = Expression.Property(parameter, propertyInfo);
        // filterModel.Term ?? String.Empty
        var term = Expression.Constant(filterModel.Term ?? String.Empty);
        //StringComparison.InvariantCultureIgnoreCase
        var comparison = Expression.Constant(StringComparison.InvariantCultureIgnoreCase);
        //T p => p.ColumnName.StartsWith(filterModel.Term ?? String.Empty, StringComparison.InvariantCultureIgnoreCase)
        var methodCall = Expression.Call(name, startsWith, term, comparison);

        var lambda = Expression.Lambda<Func<T, bool>>(methodCall, parameter);

        return input.Where(lambda.Compile())
        .Skip((filterModel.Page - 1) * filterModel.Limit)
        .Take(filterModel.Limit);
    }
}

Now this assumes that the column is of type string. Any other type would cause this to throw an exception.

You would also need to assume that p is not null else again a null reference exception will be thrown when trying to call instance members on a null reference.

Sample unit test of the extension method in use

[TestClass]
public class MyTestClass2 {
    [TestMethod]
    public void MyTestMethod() {
        //Arrange
        IEnumerable<Person> persons = new List<Person>() {
            new Person() { Name = "Nancy Davolio", DOB = DateTime.Parse("1948-12-08"), Email = "nancy.davolio@test.com" },
            new Person() { Name = "Andrew Fuller", DOB = DateTime.Parse("1952-02-19"), Email = "andrew.fuller@test.com" },
            new Person() { Name = "Janet Leverling", DOB = DateTime.Parse("1963-08-30"), Email = "janet.leverling@test.com" },
            new Person() { Name = "Margaret Peacock", DOB = DateTime.Parse("1937-09-19"), Email = "margaret.peacock@test.com" },
            new Person() { Name = "Steven Buchanan", DOB = DateTime.Parse("1955-03-04"), Email = "steven.buchanan@test.com" },
            new Person() { Name = "Michael Suyama", DOB = DateTime.Parse("1963-07-02"), Email = "michael.suyama@test.com" },
            new Person() { Name = "Robert King", DOB = DateTime.Parse("1960-05-29"), Email = "robert.king@test.com" },
            new Person() { Name = "Laura Callahan", DOB = DateTime.Parse("1958-01-09"), Email = "laura.callahan@test.com" },
            new Person() { Name = "Anne Dodsworth", DOB = DateTime.Parse("1966-01-27"), Email = "anne.dodsworth@test.com" }
        };

        var filter = new FilterModel {                
            Term = "Nancy"
        };

        //Act
        var data = persons.FilterData("Name", filter);

        //Assert
        data.Should().NotBeEmpty();
    }
}

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