Linq 查询中的异常处理
本文关键字:异常处理 查询 Linq | 更新日期: 2023-09-27 18:35:57
我正在使用...
tmpLst = (from e in App.lstAllChilds where e.Id == Id select e).ToList();
其中lstAllChilds
是列表,其中还包含一些损坏的数据。
所以现在我要在这个查询中处理 Try-Catch 块。
请帮忙。
如果您只想忽略"坏元素",那么:
App.lstAllChilds.SkipExceptions().Where( e => e.Id == Id).ToList();
扩展方法:
public static class Extensions
{
public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values)
{
using (var enumerator = values.GetEnumerator())
{
bool next = true;
while (next)
{
try
{
next = enumerator.MoveNext();
}
catch
{
continue;
}
if (next) yield return enumerator.Current;
}
}
}
}
这是您可以进行的最简单的更改,以简单地排除为 null 或引发异常的项目。
tmpLst = App.lstAllChilds.Where(e =>
{
try
{
return e != null && e.Id == Id;
}
catch
{
return false;
}
}).ToList();
但在我看来,您可能应该调查并解决根本问题。 这似乎不像是应该出现异常的情况。
所以,事情是这样的。有语言集成查询(Linq),然后是生成的枚举(又名迭代器)。
Linq 允许您定义一个表达式树,该表达式树稍后由可能是也可能不是 C# 的内容执行(例如,表达式可以转换为 SQL 查询)。如果您正在编写 linq,那么您的查询提供程序(执行表达式翻译的东西)很可能不支持异常处理(更不用说您正在执行的引发异常的任何操作了)。
另一方面,交互器(或"linq to objects")最终会在 C# 中执行,因此您可以疯狂地处理异常。
例如,使用 linq 到对象,您可以执行以下操作:
var myList = new[] { "1", "2", "BARF", "3" };
var sum = myList.Select(str => {
try {
return Int32.Parse(str);
} catch {
return 0;
}
}).Aggregate((x, y) => x + y);
如果你确实在对对象执行linq,并且你只想跳过源IEnumerable抛出异常的元素,请查看Vladimir Gondarev的答案。
然而,要了解的重要一点是,我们刚刚传递给该 Select 调用的匿名函数不是表达式(未编译的表达式树),它是一个 Func(指向已编译的 c# 代码的委托),这意味着它将在 .Net 进程中运行,即使我们将 myList 替换为 linq to entities 表(或其他一些 linq 提供程序)。原因是 C# 表达式语法不支持块,也不支持 try-catch。不出所料,鉴于此,SQL 样式的 Linq 语句(来自 xxx select yyy)也不支持 try-catch 块。
但是,仅仅因为 C# 表达式语法不支持它并不意味着你不能这样做。但是,需要明确的是,我不建议这样做,因为我非常怀疑是否存在支持它的 QueryProvider(除了 linq to 对象提供程序)。对于好奇的人,下面介绍了如何创建包含 try-catch 块的 lambda 表达式。
var parseMethod = typeof(Int32).GetMethod("Parse", new[] { typeof(String) });
var param = Expression.Parameter(typeof(String));
var selectExp =
Expression.Lambda<Func<String, Int32>>(
Expression.TryCatch(
Expression.Call(parseMethod, param),
Expression.Catch(typeof(Exception), Expression.Constant(0))
),
param
);
var sum = myList.Select(selectExp).Aggregate((x, y) => x + y);
因此,当有人实现由支持异常处理的存储支持的 QueryProvider 时,您可以使用它。
这取决于您究竟要实现什么:
-
返回除"损坏"项之外的列表。
-
您可以尝试使用
Where
子句来检查和过滤它们:App.lstAllChilds.Where(x => IsValidNode(x, Id)).ToList();
显然,您需要实现应该检查
null
IsValidNode
,如果它可以抛出InvalidObjectException
(不确定您是否可以轻松检测到它,但您始终可以将其包装在try-catch块中)并Id
相等性。诸如此类:private bool IsValidNode(Child c, int id) { if (x == null) return false; try { return c.Id == id; } catch (InvalidObjectException) { } return false; }
-
-
如果有任何损坏的项目,则返回空列表。
- 只需将整个东西包裹在一个 try-catch 块中即可