如何使用linq高效地过滤c#中包含某些属性的列表

本文关键字:包含某 属性 列表 linq 何使用 高效 过滤 | 更新日期: 2023-09-27 18:00:02

我正忙于实现一个返回设备列表的api控制器,

这是我目前的助手方法:

private List<Device> GetUsersDevices(string userName)
    {
        int userId = -1;
        List<Device> myDevices;
        if (Int32.TryParse(User.Identity.Name, out userId))
        {
            myDevices = db.Devices.Where((x => x.EndUserId == userId && x.Deleted == false)).OrderByDescending(x => x.LastComms).ToList();
            return myDevices;
        }
        return null;
    }

现在这一切都很好,但我现在需要添加一条规则,即它只返回某些设备(与最终客户端兼容的设备)

因此,我想为终端客户端列出允许的设备类型,如下面的psuedo代码:

List endclient1 = device type 1, device type 2 etc.List endclient2 = device type 2, device type 5 etc.

但我想过滤我的设备列表,以删除所有不受支持的设备。现在我明白了,我可以继续添加。Where(…)子句,但我想知道实现这一点的最有效方法,而且如果设备类型的数量增长很大,那么维护这行代码将不是很容易。我可能希望为不同的终端客户端提供不同的受支持设备列表,因此,如果我可以通过允许的设备列表进行筛选,它将允许我有一个控制器为所有不同的终端客户提供服务,并且它只会返回允许的设备。

可能需要的其他信息。

这是MyDevice类

    public class MyDevice
{        
    public MyDevice(long deviceId, string name, string serialNo, string deviceType, int deviceTypeId, bool enabled)
    {
        this.deviceId = deviceId;
        this.name = name;
        this.serialNo = serialNo;
        this.deviceTypeName = deviceType;
        this.deviceTypeId = deviceTypeId;
        this.enabled = enabled;
    }
    public string name { get; set; }
    public long deviceId { get; set; }
    public string serialNo { get; set; }
    public string deviceTypeName { get; set; }
    public int deviceTypeId { get; set; }
    public bool enabled { get; set; }
}

如何使用linq高效地过滤c#中包含某些属性的列表

如果我正确理解你的问题,你想做这样的事情:

var allowedDeviceTypes = GetAllowedDeviceTypesForUser(userId);
return db.Devices
  .Where(x => x.EndUserId == userId && x.Deleted == false)
  .Where(x => allowedDeviceTypes.Contains(x.deviceTypeId))
  .OrderByDescending(x => x.LastComms)
  .ToList();

这将被转换为WHERE deviceTypeId IN (...)子句(假设您使用的是LINQ to SQL、实体框架或NHibernate),它对于合理数量的设备类型来说足够有效。

请注意,方法GetAllowedDeviceTypesForUser与LINQ无关,您可以随心所欲地实现它(例如,将其委托给另一个服务、添加自定义规则等)。只要您有办法获得允许的设备类型列表,就可以将其传递给LINQ查询。

这里有一个琐碎的实现:

private IDictionary<int, IList<int>> _allowedDeviceTypesPerUserId = 
  new Dictionary<int, IList<int>>() {
    { 1, new[] { 1, 2 } },
    { 2, new[] { 2, 5 } },
    // ...
  };
private IList<int> GetAllowedDeviceTypesForUser(int uesrId)
{
    return _allowedDeviceTypesPerUserId[userId];
}

这是假设每个用户允许的设备列表是静态的。如果它是动态的,您将不得不从某个地方加载它,在这种情况下,您可能也会考虑使用Join,但我不确定这是否是您的最佳想法。

您可以查看join,根据键(在本例中为deviceId),为您提供"所有设备"列表和"兼容设备"列表的交集。

在这种情况下,您将执行以下操作(未检查语法正确性):

db.Devices.Join(endClientList, d => d.deviceId, ecd => ecd.deviceId, d => d);

这只是我的想法,所以可能会有一些错误,但如果你想的话,你应该能够想出如何使用它。

除非你有几十亿台设备,否则我认为你会发现普通的旧LINQ效率更高:

myDevices = from device in db.Devices
            where device.EndUserId == userId 
              && device.Deleted == false
              && allowedTypes.Contains(device.Type)
            order by device.LastComms
            select device;

它也是可读性和可维护性最高的!

这方面的另一个优点是标准LINQ是编译器抖动到SQL的转换都可以优化这些标准的东西-你测量过它的速度吗-这是一个问题吗?你把它与其他选择进行了比较吗?

我不确定我是否理解你的问题。但我会这样做:

    int userId = -1;
    List<Device> myDevices;
    if (Int32.TryParse(User.Identity.Name, out userId))
    {
        myDevices = db.Devices.Where((x => x.EndUserId == userId && x.Deleted == false)).OrderByDescending(x => x.LastComms).ToList();
        var result1 = myDevices.Where(x=>x.name==someName);
        var result2 = myDevices.Where(x=>x.deviceId >someDeviceId );
        var result3 = myDevices.Where(x=>x.serialNo.Contains("someSerialNo"));
        ....
        return resultN.ToList();
    }

myDevices = from item in db.Devices
            where item.name == someName && item.deviceId == someDeviceId && ...
            where ...
            OrderByDescending item.LastComms
            select item;