Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# SingleOrDefault异常处理_C#_.net_Linq - Fatal编程技术网

C# SingleOrDefault异常处理

C# SingleOrDefault异常处理,c#,.net,linq,C#,.net,Linq,我有一个samle代码,它调用SingleOrDefault方法3次,并在任何序列有多个匹配元素时记录异常 如果我想检查这段代码的哪一部分引发异常,问题就开始了 是否有可能从该异常中获取一些有用的信息,如谓词参数或集合类型,以便进行更详细的跟踪 与此类似-序列包含多个匹配元素。集合IEnumerable | ParamType | param{谓词param toString()} 无论何时使用SingleOrDefault,您都清楚地声明查询最多只能产生一个结果。另一方面,当使用FirstOr

我有一个samle代码,它调用SingleOrDefault方法3次,并在任何序列有多个匹配元素时记录异常

如果我想检查这段代码的哪一部分引发异常,问题就开始了

是否有可能从该异常中获取一些有用的信息,如谓词参数或集合类型,以便进行更详细的跟踪

与此类似-序列包含多个匹配元素。集合IEnumerable | ParamType | param{谓词param toString()}


无论何时使用SingleOrDefault,您都清楚地声明查询最多只能产生一个结果。另一方面,当使用FirstOrDefault时,查询可以返回任意数量的结果,但您声明只需要第一个结果

我个人发现语义非常不同,根据预期结果使用适当的语义可以提高可读性


没有理由使用SingleLordDefault。我会将其重构为:

var user = Users.Count(e => e.Id == 1);
var profile = UserProfiles.Count(e => e.Id == 1);
var profile2 = UserProfiles.Count(e => e.Id == 2);

if(user + profile + profile2 != 3){
  Log("more than one");
}

这基本上是相同的,但不是异常驱动的。在你的问题中,我看不出使用异常驱动编程的理由。

如果你想知道哪个语句会发出错误,你必须分别检查它们。捕获每个
SingleOrDefault
调用上的
InvalidOperationException
,并将其包装在一个新的异常中,您可以用其他信息填充该异常

try
{
    User user;
    UserProfile profile;
    UserProfile profile2;

    try
    {
        user = Users.SingleOrDefault(e => e.Id == 1);
    }
    catch (InvalidOperationException ex)
    {
        throw new InvalidOperationException("User lookup for Id = 1 failed", ex);
    }

    try
    {
        profile = UserProfiles.SingleOrDefault(e => e.Id == 1);
    }
    catch (InvalidOperationException ex)
    {
        throw new InvalidOperationException("User profile lookup for Id = 1 failed", ex);
    }

    try
    {
        profile2 = UserProfiles.SingleOrDefault(e => e.Id == 2);
    }
    catch (InvalidOperationException ex)
    {
        throw new InvalidOperationException("User profile lookup for Id = 2 failed", ex);
    }

    // work with user, profile and profile2
}
catch(InvalidOperationException ex)
{
    Log(ex);
}
编辑:

您还可以通过以下方式封装单次尝试捕获

private static T GetSingleOrDefault<T>(IEnumerable<T> collection, Expression<Func<T, bool>> predicate)
{
    try
    {
        return collection.SingleOrDefault(predicate.Compile());
    }
    catch (InvalidOperationException e)
    {
        var message = string.Format(
            "{0} (Collection: {1}, param: {2})",
            e.Message,
            collection.GetType(),
            predicate);

        throw new InvalidOperationException(message);
    }
}
这会产生如下消息:

System.InvalidOperationException:序列包含多个匹配元素(集合:IEnumerable`1[User],参数:e=>e.Id==1)


在我回答之前:请注意在这里使用异常处理的危险。在对这一问题的评论中,我指出了几个问题。最后,您可能会记录误导性信息,从而恶化日志输出

try
{
    User user;
    UserProfile profile;
    UserProfile profile2;

    try
    {
        user = Users.SingleOrDefault(e => e.Id == 1);
    }
    catch (InvalidOperationException ex)
    {
        throw new InvalidOperationException("User lookup for Id = 1 failed", ex);
    }

    try
    {
        profile = UserProfiles.SingleOrDefault(e => e.Id == 1);
    }
    catch (InvalidOperationException ex)
    {
        throw new InvalidOperationException("User profile lookup for Id = 1 failed", ex);
    }

    try
    {
        profile2 = UserProfiles.SingleOrDefault(e => e.Id == 2);
    }
    catch (InvalidOperationException ex)
    {
        throw new InvalidOperationException("User profile lookup for Id = 2 failed", ex);
    }

    // work with user, profile and profile2
}
catch(InvalidOperationException ex)
{
    Log(ex);
}
对于控制流,不要依赖异常处理。你会意外地捕捉到真正的bug并隐藏它们,还有其他问题

异常也有非常糟糕的性能

使用异常处理来诊断特定问题非常容易出错。让我们看看能不能不用它就走

public static TSource SingleOrDefaultWithDiagnostics<TSource>(
  this IEnumerable<TSource> source,
  Func<TSource, bool> predicate,
  string failureMessage) {

    using (IEnumerator<TSource> enumerator = source.Where(predicate).GetEnumerator())
    {
        if (!enumerator.MoveNext())
        {
            return default(TSource);
        }
        TSource current = enumerator.Current;
        if (!enumerator.MoveNext())
        {
            return current;
        }
        else throw new SingleElementException(failureMessage);
    }
}

这就是日志记录进入场景的地方(用法示例,可从NuGet获得):

只要看一下日志文件,就会知道哪条语句失败了:

2014-05-26 12:04:48.8655 DEBUG Getting user with id = 1
2014-05-26 12:04:48.8815 DEBUG Getting user profile with id = 1
2014-05-26 12:04:48.8815 DEBUG Getting user profile with id = 2
2014-05-26 12:04:48.8815 ERROR Failed getting single or default
Sequence contains more than one matching element

看:你一开始就做错了。对于控制流,不要依赖异常处理。您会意外地发现真正的bug并将其隐藏起来,还有其他问题。@usr仅从这段代码中我不清楚它是否被用于控制flow@BenAaronson如果异常是执行的预期部分(并且可以避免),那么它们将用于控制流。这就是我的定义。@usr是的,我只是不确定在这种情况下是不是这样。为什么具有相同ID的多个用户是执行的预期部分?这看起来像是标准的“检查我的数据没有以某种方式进入一种奇怪的意外状态”代码。你能解释一下这个方法的要点吗?看起来您刚刚重新实现了
SingleOrDefault
@BenAaronson您现在可以从异常中准确地判断发生了什么。与原始版本不同,原始版本抛出了一个未区分的IOE。
SingleOrDefault
的文档说,如果“源代码有多个元素”,它就会抛出一个IOE。这与您的相同,只是您使用了不同的异常类型。谓词也可以抛出,或者可能有一些周围的代码也可以抛出。但在这种情况下,这两个都不是真的(是的,好吧,
Id
可能有一些奇怪的
get
逻辑,但几乎可以肯定不是)。作为这个问题的答案,这是一个非常令人困惑的建议。这是真的,但在这种情况下,我认为
SingleOrDefault
才是他真正想要的
 var user = Users.SingleOrDefaultWithDiagnostics(e => e.Id == 1, "User with Id 1");
 var profile = UserProfiles.SingleOrDefaultWithDiagnostics(e => e.Id == 1, "...");
 var profile2 = UserProfiles.SingleOrDefaultWithDiagnostics(e => e.Id == 2, "...");
public void GetSingleOrDefaultTest()
{
    try
    {
        Logger.Debug("Getting user with id = {0}", 1);
        var user = Users.SingleOrDefault(e => e.Id == 1);

        Logger.Debug("Getting user profile with id = {0}", 1);
        var profile = UserProfiles.SingleOrDefault(e => e.Id == 1);

        Logger.Debug("Getting user profile with id = {0}", 2);
        var profile2 = UserProfiles.SingleOrDefault(e => e.Id == 2);
    } 
    catch(Exception ex)
    {
        Logger.ErrorException("Failed getting single or default", ex);
    }
}
2014-05-26 12:04:48.8655 DEBUG Getting user with id = 1
2014-05-26 12:04:48.8815 DEBUG Getting user profile with id = 1
2014-05-26 12:04:48.8815 DEBUG Getting user profile with id = 2
2014-05-26 12:04:48.8815 ERROR Failed getting single or default
Sequence contains more than one matching element