首页 > 解决方案 > C# 处理 DAL 中的 WebExceptions

问题描述

有没有办法在数据访问层中正确处理 WebExceptions?

下面是SendReceive我们的 DAL 中用于与远程服务器通信的方法,如果存在通信问题,例如无法访问端点,因此无法检索数据,我希望将用户重定向到视图,通知用户请稍后再试。

private static TResult SendReceive<TResult, TPayLoad>(string method, string route, TPayLoad payload, bool post, bool authentication, string hashedPassword)
{
    var subject = "WebApplication1 - " + method + " Error";

    using (var webClient = new WebClient())
    {
        try
        {
            var uri = new Uri("http://ourdomain/ourwebapicontroller/" + route);

            webClient.Headers[HttpRequestHeader.ContentType] = "application/json";

            if (authentication)
            {
                var hashedPasswordAsBytes = Encoding.UTF8.GetBytes(hashedPassword);

                webClient.Headers.Add(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(hashedPasswordAsBytes));
            }

            var response = post ? webClient.UploadString(uri, JsonConvert.SerializeObject(payload)) : webClient.DownloadString(uri);

            var parsedResponse = JsonConvert.DeserializeObject<TResult>(response);

            return parsedResponse;
        }
        catch (WebException webException)
        {
            SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>WebException [" + webException.Message + "]</p>");

            // Issue with endpoint 
        }
        catch (Exception exception)
        {
            SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>Exception [" + exception.Message + "]</p>");
        }
    }

    return default(TResult);
}

public Models.WebApplication1.Test GetTest(int id)
{
    return SendReceive<Models.WebApplication1.Test, int?>("GetTest", "get-test/" + id, null, false, false, null);
}

public int SetTest(Models.WebApplication1.Test test)
{
    return SendReceive<int, Models.WebApplication1.Test>("SetTest", "set-test", test, true, false, null);
}    

由于 DAL 是从 Controller 引用的,我不相信它可以使用throw new HttpException(),但是可以这样处理:

public ViewResult Test(int id)
{
    var test = Dal.GetTest(id);

    if (test == null)
    {
        throw new HttpException(404, "Please try again later.");
    }

    return View(test);
}

宁愿在内部处理通信问题SendReceive,而不是在控制器级别处理每个方法引用SendReceive

标签: c#data-access-layersystem.net.webexception

解决方案


一切都取决于您所说的“处理”甚至“异常”是什么意思。

控制器
在控制器中,如果客户端请求不存在的东西,你想做什么?404 是一个很好的响应。但是如果 DAL 抛出异常怎么办?将完全相同的结果返回给客户端是否有意义?告诉客户出了问题的 500 错误可能更有意义。

此处指出了这种不匹配:

throw new HttpException(404, "Please try again later.");

如果请求抛出异常(出于任何原因,包括 DAL),则返回 500 错误并带有“稍后再试”是有意义的。您正在清楚地传达问题出在您身上。对不起,希望它不会再发生,如果我们正在努力。

如果客户请求了一些不存在的东西,那么这可能会也可能不会改变。他们应该稍后再试吗?为什么?也许他们所要求的永远不会被发现。这也不例外。有人要求一些不存在的东西却一无所获,这意味着您的应用程序正在正常工作。404 告诉他们我们的应用程序正在运行——我们只是没有他们想要的东西。

基于此,将实际异常冒泡到控制器可能是有意义的。DAL 不知道控制器甚至网站。不知道调用者是否应该知道存在异常。

DAL “处理”异常可能意味着不同的事情。(我会省略我对哪个是正确的看法,因为它不相关。)

如果你的 DAL 抛出异常,你可以做一些事情。有些可能比其他更好,但同样,这取决于意见和需求。- 没做什么。让异常冒泡。- 记录并重新抛出它。- 记录它,然后将它包装在另一个提供一些上下文的异常中,并抛出新的异常。(是否包装异常是一个完整的讨论。)

有人会说“处理”异常是不同的东西,它涉及以某种方式对异常做出反应以解决问题,这是我们不太可能做的事情。例如,如果我们的应用程序从 API 检索每日 Chuck Norris 笑话但抛出异常,我们可能会记录它以便我们知道出了什么问题,然后用备份 Chuck Norris 笑话替换它。

我不会做的最重要的事情是“隐藏”异常,以便对调用者来说,异常和“未找到”看起来相同。如果出现问题,控制器需要知道——即使它不了解细节——所以它(不是 DAL)——可以确定应该与调用者通信的内容。

控制器与 DAL 之间的关系类似于浏览器客户端与控制器之间的关系。如果它不只是工作,我们会沟通。如果没有数据,我们会进行沟通。


我不建议将编写代码放在发送电子邮件的 DAL 中。这是非常具体的,它将您的所有代码与该决定以及可能与发送邮件的实现相结合。

另一种方法是定义这样的接口:

public interface ILog
{
    void LogException(Exception ex);
    void LogMessage(string message);
}

...并注入 DAL 类。发生异常时,调用_log.LogException(ex);. 您的 DAL 不知道实现是什么。如果您愿意,您可以记录它甚至发送电子邮件。


推荐阅读