C# 使用LINQ在列表中迭代智能方式
我有一个简单的课程:C# 使用LINQ在列表中迭代智能方式,c#,list,linq-to-objects,C#,List,Linq To Objects,我有一个简单的课程: public class JDEItemLotAvailability { public string Code { get; set; } public int ShortCode { get; set; } public string Description { get; set; } public string PrimaryUnitCode { get; set; } publi
public class JDEItemLotAvailability
{
public string Code { get; set; }
public int ShortCode { get; set; }
public string Description { get; set; }
public string PrimaryUnitCode { get; set; }
public string BranchPlant { get; set; }
public string Location { get; set; }
public string Lot { get; set; }
public int AvailableQuantity { get; set; }
}
my BLL中的此DAL方法返回它们的列表:
var returnedLotList = _JDE8dal.GetLotAvailabilityAsList(_lot);
我希望在返回的列表中执行以下操作,并希望以最“优雅”的LINQ方式执行
我想检查列表中是否有符合特定条件的记录。我想到过这样的事情:
var query =
returnedLotList.Where(l => l.AvailableQuantity != 0 && l.BranchPlant == _mcu && l.Location == _locn)
.OrderByDescending(l => l.AvailableQuantity);
var firstMatch = returnedLotList.FirstOrDefault(l => l.AvailableQuantity != 0 &&
l.BranchPlant == _mcu &&
l.Location == _locn);
if (firstMatch != null)
return firstMatch;
int max = returnedLotList.Max(l => l.AvailableQuantity);
return returnedLotList.First(l => l.AvailableQuantity == max);
但是我想说的是,如果上面的查询没有返回结果,那么我希望使用列表中剩余的第一个条目
我如何才能做到这一点?您可以创建一个扩展方法来实现这一点:
public static IEnumerable<T> WhereOrFirstOfRest<T>(
this IEnumerable<T> collection, Func<T, bool> predicate)
{
var filtered = collection.Where(predicate);
return filtered.Any() ? filtered : collection.Take(1);
}
公共静态IEnumerable WhereOrFirstOfRest(
此IEnumerable集合(Func谓词)
{
var filtered=collection.Where(谓词);
返回filtered.Any()?filtered:collection.Take(1);
}
这种扩展方法的缺点是它多次迭代集合。在处理流(例如来自数据库)时,这可能是一个问题。更有效的方法如下:
public static IEnumerable<T> WhereOrFirstOfRest<T>(
this IEnumerable<T> collection, Func<T, bool> predicate)
{
// Materialize the complete collection.
collection = collection.ToArray();
// Filter the collection. ToArray prevents calling the predicate
// twice for any item.
var filtered = collection.Where(predicate).ToArray();
return filtered.Any() ? filtered : collection.Take(1);
}
public static IEnumerable<T> WhereOrFirstOfRest<T>(
this IEnumerable<T> collection, Func<T, bool> predicate)
{
T firstItem = default(T);
bool firstStored = false;
bool predicateReturnedItems = false;
foreach (var item in collection)
{
if (!firstStored)
{
firstItem = item;
firstStored = true;
}
if (predicate(item))
{
yield return item;
predicateReturnedItems = true;
}
}
if (!predicateReturnedItems && !first)
{
yield return firstItem;
}
}
公共静态IEnumerable WhereOrFirstOfRest(
此IEnumerable集合(Func谓词)
{
//实现整个集合。
collection=collection.ToArray();
//筛选集合。ToArray阻止调用谓词
//任何项目两次。
var filtered=collection.Where(谓词).ToArray();
返回filtered.Any()?filtered:collection.Take(1);
}
尽管这样可以避免对数据库进行任何可能的额外调用,但这确实会在封面下创建几个新阵列。因此,最有效的方法是:
public static IEnumerable<T> WhereOrFirstOfRest<T>(
this IEnumerable<T> collection, Func<T, bool> predicate)
{
// Materialize the complete collection.
collection = collection.ToArray();
// Filter the collection. ToArray prevents calling the predicate
// twice for any item.
var filtered = collection.Where(predicate).ToArray();
return filtered.Any() ? filtered : collection.Take(1);
}
public static IEnumerable<T> WhereOrFirstOfRest<T>(
this IEnumerable<T> collection, Func<T, bool> predicate)
{
T firstItem = default(T);
bool firstStored = false;
bool predicateReturnedItems = false;
foreach (var item in collection)
{
if (!firstStored)
{
firstItem = item;
firstStored = true;
}
if (predicate(item))
{
yield return item;
predicateReturnedItems = true;
}
}
if (!predicateReturnedItems && !first)
{
yield return firstItem;
}
}
公共静态IEnumerable WhereOrFirstOfRest(
此IEnumerable集合(Func谓词)
{
T firstItem=默认值(T);
bool firstStored=false;
bool predicateReturnedItems=false;
foreach(集合中的var项)
{
如果(!firstStored)
{
firstItem=项目;
firstStored=true;
}
if(谓语(项))
{
收益回报项目;
predicateReturnedItems=true;
}
}
if(!predicateReturnedItems&&!first)
{
第一项收益率;
}
}
您可以使用DefaultIfEmpty
//your first query, unaltered
var query =
returnedLotList.Where(l => l.AvailableQuantity != 0 && l.BranchPlant == _mcu && l.Location == _locn)
.OrderByDescending(l => l.AvailableQuantity);
var query2 = query.DefaultIfEmpty(returnedLotList.Take(1));
我不确定我是否理解,但可能是这样的:
var query =
returnedLotList.Where(l => l.AvailableQuantity != 0 && l.BranchPlant == _mcu && l.Location == _locn)
.OrderByDescending(l => l.AvailableQuantity);
var firstMatch = returnedLotList.FirstOrDefault(l => l.AvailableQuantity != 0 &&
l.BranchPlant == _mcu &&
l.Location == _locn);
if (firstMatch != null)
return firstMatch;
int max = returnedLotList.Max(l => l.AvailableQuantity);
return returnedLotList.First(l => l.AvailableQuantity == max);
- 如果不匹配,FirstOrDefault将返回null
- 我认为将查询分开是一个好主意,以便更清楚地了解正在发生的事情
任何
,一次用于实际结果。当迭代(甚至只获取一个项目)涉及到数据库的往返时,这是一个问题。@Servy:这绝对正确。在我读你的评论之前,我已经在写一个改进版了。我们在同一页:-)你试图同时做很多事。正如我的回答所示,DefaultIfEmpty已经为您完成了大部分这一切,您主要只是重新实现它,并在Where
中混合使用,而不是单独调用两个现有函数。如果这真的足够让它拥有自己的功能(我怀疑),你仍然可以重复使用这些方法。整个实现可以是collection.Where(谓词).DefaultIfEmpty(collection.Take(1))代码>给定了您定义的定义。@Servy:请参阅我对您答案的评论。您的代码还将触发第二个查询:-),并查看我的响应。此代码将要求将整个集合拉入内存,甚至是所有不符合筛选条件的项。如果不使用Where
,则不允许对数据库执行该筛选。请注意,如果筛选列表为空,则会多次迭代查询。一次用于DefaultIfEmpty
,一次用于运行Take(1)
。当迭代(即使只获取一个项目)涉及到数据库的往返时,这是一个问题。:-)@史蒂文:实际上,如果where
过滤了很大一部分内容,这是可取的。首先执行一个查询,在数据库端进行过滤。如果它没有返回任何项目,则对一个项目执行另一个查询。在单个查询中执行此操作的唯一方法是获取所有项,即使是不符合条件的项。这是一个巨大的网络开销。另外,根据问题描述,常见的情况是文件管理器返回一些东西,因此该常见情况仅迭代结果一次。,其中
将始终在.NET中执行,在这种情况下,不会在数据库中执行。但是,当我们谈论表达式树和使用Queryable
时,在这种情况下,您是对的,这将是最有效的。@Steven然后多次迭代它的结果足够小(在本文中)以至于无关紧要,因为您只在迭代多次的唯一情况下获取第一项。关键是answe