如何使用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; }
}
如果我正确理解你的问题,你想做这样的事情:
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;