【问题标题】:Dynamic filtering using Linq使用 Linq 进行动态过滤
【发布时间】:2014-02-10 11:11:03
【问题描述】:

我有一个List<Device>。在Device类中有4个属性,分别是NameOperatingSystemStatusLastLoggedInUser。我需要写一个方法:

IQueryable<Device> FilterDeviceList(
    List<Device> Devices,
    List<string> filter,
    string filterValue)

其中filter 将包含用于过滤"name""os" 的选项以指示要包含在搜索中的字段。如果 "all" 被传递,则需要包含所有 4 个字段。 filtervalue 将包含要过滤的值,例如 "windows""Calvin"

谁能建议一种方法来实现这一点?

编辑:

如果我不清楚,我正在像这样进行过滤,这是我需要代码的注释部分。

if(filter.contains(name))
{
//filter with name
}
if( filter.contains(both name and os)
{
// I only need the filter value to contain in name or OS (only needed in any one of the field,not necessary to be in both)

}`

【问题讨论】:

  • 你总是可以用 if 语句做到这一点,但我不知道它是否可以用动态 LINQ 做到。

标签: c# linq


【解决方案1】:

您可以按如下方式构建查询:

private static IQueryable<Device> FilterDeviceList(List<Device> devices, Device device)
{
    var query = devices.AsQueryable();

    if (device.Name != null)
        query = query.Where(d => d.Name == device.Name);

    if (device.OS != null)
        query = query.Where(d => d.OS == device.OS);

    if (device.Status != null)
        query = query.Where(d => d.Status == device.Status);

    if (device.LastLoggedInUser != null)
        query = query.Where(d => d.LastLoggedInUser == device.LastLoggedInUser);

    return query;
}

然后您可以使用设备对象调用此函数。 IE。如果您想包含名称,只需传递一个带有名称的设备对象(将其他属性保留为默认值)。如果您希望包含所有内容,请传入一个填充了所有内容的设备对象:

var r = FilterDeviceList(devices, new Device
            {
                Name = "yourFilterValue",
                OS = "yourFilterValue",
                LastLoggedInUser = "yourFilterValue",
                Status = "yourFilterValue"
            });

编辑、过滤名称属性:

var r = FilterDeviceList(devices, new Device
                {
                    Name = "yourFilterValue"
                });

编辑2,看看predicatebuilder

var predicate = PredicateBuilder.False<Device>();

if(document.Name != null)
    predicate = predicate.Or(d => d.Name == document.Name);

if(document.OS != null)
    predicate = predicate.Or(d => d.OS == document.OS);

return devices.Where(predicate);

【讨论】:

  • 有趣的想法。我喜欢它。
  • @dieter 感谢您的快速回复。我已经编辑了,请你看一下,我需要实现这一点
  • 在我的示例中,您不需要“过滤器”列表。如果您希望包含所有内容,只需传入一个填充了所有属性的对象。如果您只想过滤名称,请传入一个仅填写属性名称的对象。
  • 我试过你的代码,但我得到的是 alldevices.where(firstcondition).where(second condition)。但我需要的是 if(filters.contain(Name)) alldevices.where(name == value) if(filters.contain(name,OS) alldevices.where(name||os == value)
  • 查看更新的答案,看看谓词生成器
【解决方案2】:

你可以这样做:

public IEnumerable<Device> FilterDevices(IEnumerable<Device> devices, IEnumerable<Func<Device, string>> filters, string filterValue)
{
    foreach (var filter in filters)
    {
        devices = devices.Where(d => filter(d).Equals(filterValue));
    }

    return devices;
}

与:

public class Device
{
    public string Name { get; set; }
    public string OS { get; set; }
}

用法:

var devices = new List<Device>
{
    new Device { OS = "Windows", Name = "Foo" },
    new Device { OS = "Mac", Name = "Bar" }
};

var filters = new List<Func<Device, string>>
{
    d => d.OS
};

var result = FilterDevices(devices, filters, "Windows");

这只是一个粗略的想法 - 根据需要转换为您的解决方案!

【讨论】:

    【解决方案3】:

    你基本上有两个选择:

    1. 使用 System.Linq.Expression API 来构建您的 LINQ 谓词(虽然工作量很大,但如果操作正确,这是一个很好的可重用解决方案)。您仍然需要将过滤器名称和属性映射到某些东西(例如同名)
    2. 硬编码过滤器名称和Func&lt;TObject, TFilterValue, bool&gt; 谓词之间的映射。您可以在静态字典(或普通的旧开关)中执行此操作。只需确保您在谓词上创建一个闭包并绑定您的过滤器值。 Where 调用需要 Func&lt;T, bool&gt;

    【讨论】:

      【解决方案4】:
      private IQueryable<Device> FilterCentraStageDeviceList(List<Device> centraStageDevices, string filter, string filterValue)
          {
              IQueryable<Device> alldevices = centraStageDevices.AsQueryable<Device>();
              IQueryable<Device> query = new List<Device>().AsQueryable();
      
              if (filter == null || string.IsNullOrEmpty(filterValue))
              {
                  return alldevices;
              }
      
              filterValue = filterValue.ToLower();          
              var filterLower = filter.ToLower();
      
              if (filterLower.Contains("all") || (filterLower.Contains("hostname") && filterLower.Contains("operatingsystem") && filterLower.Contains("status") && filterLower.Contains("lastloggedinuser")))
              {
                  return alldevices.Where(x => checkNull(x.Name).Contains(filterValue) || checkNull(x.OperatingSystem).Contains(filterValue) || checkNull(x.LastUser).Contains(filterValue));               
              }
      
              if (filterLower.Contains("hostname"))
              {
                  query = alldevices.Where(x => checkNull(x.Name).Contains(filterValue));
              }
      
              if (filterLower.Contains("operatingsystem"))
              {
                  query = alldevices.Where(x => checkNull(x.OperatingSystem).Contains(filterValue)).Union(query);
              }
      
              if (filterLower.Contains("lastloggedinuser"))
              {
                  query = alldevices.Where(x => checkNull(x.LastUser).Contains(filterValue)).Union(query);
              }            
      
              return query;
          }
      

      这是我最终使用的,因为不允许我使用外部 dll,即使谓词构建器是我的场景的合适解决方案。

      【讨论】:

        猜你喜欢
        • 2018-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-31
        • 2019-11-27
        • 1970-01-01
        • 2012-08-13
        • 1970-01-01
        相关资源
        最近更新 更多