【问题标题】:Linq query - "Where" on List<T> Where reflected property Contains text/valueLinq 查询 - List<T> 上的“Where” 反射属性包含文本/值
【发布时间】:2018-03-09 02:39:32
【问题描述】:

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

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

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 的反射来扩展该解决方案并将其用于表达式树。

【问题讨论】:

标签: c# linq expression-trees


【解决方案1】:

您可以将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"] 检索值,甚至可以设置它们的值!

【讨论】:

    【解决方案2】:

    您可以使用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

    【讨论】:

    • 好答案,你可以使用OrdinalIgnoreCase而不是ToLower
    • 是的 IndexOf and Ignore case is going to be more efficientToLower(ing) 一切。
    • 谢谢!没想到简单的反思就能让我完成任务。我将您的答案标记为已接受,因为它在类型转换方面很严格并且不需要扩展方法
    【解决方案3】:

    泛型和 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; }
        }
    }
    

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

    【讨论】:

    • 谢谢,在这种情况下我不能使用它,但它对未来的良好教训;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    相关资源
    最近更新 更多