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

时间:2014-04-24 07:39:07

标签: c# linq

我正忙着实现一个返回设备列表的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;
    }

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

所以我想列出一个终端客户端允许的设备类型列表,如下面的伪代码:

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; }
}

4 个答案:

答案 0 :(得分:1)

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

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,Entity Framework或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,但我不确定这是最好的主意在你的情况下。

答案 1 :(得分:0)

您可以查看join,根据密钥(“{1}}”为您提供“所有设备”列表和“兼容设备”列表的交集。

在这种情况下,你会做类似的事情(没有检查语法正确性):

deviceId

这只是我的头脑,所以可能会有一些错误,但你应该能够弄清楚如何使用它。

答案 2 :(得分:0)

除非你有几十亿台设备,否则我认为你发现普通的旧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可以全部优化这个标准的东西 - 你测量它的速度 - 它是一个问题,你把它与替代品进行比较?

答案 3 :(得分:0)

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

    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;