Single上调用SingleOrDefaultIEnumerable<T>时,如果结果不只一个,则会抛出InvalidOperationException

尽管异常的实际消息具有很强的描述性,但是编写一个仅处理Single/SingleOrDefault调用失败的情况的catch是有问题的。

public virtual Fee GetFeeByPromoCode(string promoCode)
{
    try
    {
        return _fees.SingleOrDefault(f => f.IsPromoCodeValid(promoCode));
    }
    catch (InvalidOperationException)
    {
        throw new TooManyFeesException();
    }
}

在这种情况下,如果IsPromoCodeValid也抛出InvalidOperationException,那么对于捕获正在处理的内容就变得模棱两可。

我可以检查异常消息,但是我想避免这种情况,因为我发现根据异常消息来处理代码很脏。

我当前对SingleOrDefault的替代方案如下所示:
public virtual Fee GetFeeByPromoCode(string promoCode)
{
    var fees = _fees.Where(f => f.IsPromoCodeValid(promoCode)).ToList();

    if (fees.Count > 1)
    {
        throw new InvalidFeeSetupException();
    }

    return fees.FirstOrDefault();
}

但是,此代码与上面的代码相比不那么明显,此外,与使用SingleOrDefault相比,此查询(如果使用启用了linq的ORM)生成的查询效率较低。

我也可以在第二个示例中执行Take(2)来对其进行一些优化,但这进一步混淆了代码的意图。

有没有一种方法,而无需为IEnumerableIQueryable编写我自己的扩展名?

最佳答案

这样可以解决问题吗?

public virtual Fee GetFeeByPromoCode(string promoCode)
{
    try
    {
        return _fees.SingleOrDefault(f =>
            {
                try
                {
                    return f.IsPromoCodeValid(promoCode);
                }
                catch(InvalidOperationException)
                {
                    throw new PromoCodeException();
                }
            });
    }
    catch (InvalidOperationException)
    {
        throw new TooManyFeesException();
    }
}

10-08 16:51