Linq查询 - 列表上的“Where”<t>其中reflect属性包含文本/值

时间:2017-09-27 14:39:27

标签: c# linq expression-trees

我想构建一个函数,用户可以搜索列表中的某些属性是否包含值

假设我们将拥有List,公司将被定义为具有以下属性的类:

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string CompanyAddress1 { get; set; }
    public string CompanyPostCode { get; set; }
    public string CompanyCity { get; set; }
    public string CompanyCounty { get; set; }
}

现在 - 理想情况下我想使用这个参数

List<Company> FilterCompanies(List<Company> unfilteredList, string fieldToQueryOn, string query)
{
    // linq  version what ideally would like to archeve
    return unfilteredList.Where(x => x."fieldToQueryOn".ToString().ToLower().Contains(query.ToLower())).ToList();
}

并致电:

var variable = FilterCompanies(NotNullFilledUnfilteredList, "CompanyCity", "New York")

我尝试按照docs.microsoft.com上的教程进行操作,这很容易,但我不知道如何使用对Type的反射扩展该解决方案并在表达式树中使用它。

3 个答案:

答案 0 :(得分:5)

泛型和lambda:

namespace WhereTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var companies = new[] { new Company { Id = 1, Name = "abc" }, new Company { Id = 2, CompanyAddress1 = "abc" } };
            foreach (var company in FilterCompanies(companies, "abc", x => x.Name, x => x.CompanyCity))
            {
                Console.WriteLine(company.Id);
            }
        }

        static List<Company> FilterCompanies(IEnumerable<Company> unfilteredList, string query, params Func<Company, string>[] properties)
        {
            return unfilteredList.Where(x => properties.Any(c => c.Invoke(x) == query)).ToList();
        }
    }

    public class Company
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string CompanyAddress1 { get; set; }
        public string CompanyPostCode { get; set; }
        public string CompanyCity { get; set; }
        public string CompanyCounty { get; set; }
    }
}

优点:没有反射,强类型代码。

答案 1 :(得分:4)

您可以使用Type.GetProperty按名称使用反射查找属性,然后使用GetValue检索值:

List<Company> FilterCompanies(List<Company> list, string propertyName, string query)
{
    var pi = typeof(Company).GetProperty(propertyName);

    query = query.ToLower();
    return list
        .Where(x => pi.GetValue(x).ToString().ToLower().Contains(query))
        .ToList();
}

如果有人使用无效的属性,您应该添加一些错误处理。例如,您可以(pi?.GetValue(x) ?? string.Empty).ToString().ToLower()…安全地使用。

我还将query.ToLower()移出了lambda表达式,以确保它只运行一次。您还可以尝试其他不区分大小写的方法来检查query是否是值的子字符串,以避免必须转换任何字符串。有关详细信息,请查看问题“Case insensitive 'Contains(string)'”

顺便说一下。如果您通常对运行动态查询感兴趣,则应该查看dynamic LINQ

答案 2 :(得分:0)

您可以将GetPropertyGetValue

结合使用
List<Company> FilterCompanies(List<Company> unfilteredList, string fieldToQueryOn, string query)
{
   return unfilteredList
       .Where(x => x.GetType.GetProperty(fieldToQueryOn).GetValue(x)
       .ToString().ToLower().Contains(query.ToLower())).ToList();
}

或:property accessors using string(与javascript obj[property]相同)

您可以修改您的课程:

public class Company
{
    // just add this code block to all your classes that would need to access
    // your function
    public object this[string propertyName] 
    {
        get{
            Type myType = typeof(Company);                   
            PropertyInfo myPropInfo = myType.GetProperty(propertyName);
            return myPropInfo.GetValue(this, null);
        }
        set{
            Type myType = typeof(Company);                   
            PropertyInfo myPropInfo = myType.GetProperty(propertyName);
            myPropInfo.SetValue(this, value, null);
        }

    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string CompanyAddress1 { get; set; }
    public string CompanyPostCode { get; set; }
    public string CompanyCity { get; set; }
    public string CompanyCounty { get; set; }
}

然后您可以像这样更改您的功能:

List<Company> FilterCompanies(List<Company> unfilteredList, string key, string query)
{
    // linq  version what ideally would like to archeve
    return unfilteredList.Where(x => x[key].ToString().ToLower().Contains(query.ToLower())).ToList();
}

选中此Demo

注意:

为了使您的功能正常工作,您需要将此代码添加到您的类中:

public object this[string propertyName] 
{
    get{
        Type myType = typeof(<YOUR CLASS HERE>);                   
        PropertyInfo myPropInfo = myType.GetProperty(propertyName);
        return myPropInfo.GetValue(this, null);
    }
    set{
        Type myType = typeof(<YOUR CLASS HERE>);                   
        PropertyInfo myPropInfo = myType.GetProperty(propertyName);
        myPropInfo.SetValue(this, value, null);
    }

}

加分:您现在可以使用myObject["someproperty"]检索值,甚至可以设置其值!