Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.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# 使用DDD和Azure进行优雅的异常处理_C#_Azure_Exception Handling_Domain Driven Design - Fatal编程技术网

C# 使用DDD和Azure进行优雅的异常处理

C# 使用DDD和Azure进行优雅的异常处理,c#,azure,exception-handling,domain-driven-design,C#,Azure,Exception Handling,Domain Driven Design,我正在使用领域驱动设计开发一个MVC5Web应用程序。我的控制器基本上对服务层进行调用,该服务层返回数据实体或实体列表,或根据场景执行操作业务流程 这是我的困惑。我需要一个有效的策略来记录发生的异常,以便进行故障排除,同时向用户显示友好消息,或者在某些情况下根本不显示 例如,假设服务层中的某些代码导致NullReferenceException,我希望在记录异常以进行故障排除时为用户优雅地处理此问题。此外,假设存储库层中发生异常,例如在尝试访问数据库时发生连接错误。这将是另一个我想以同样方式处理

我正在使用领域驱动设计开发一个MVC5Web应用程序。我的控制器基本上对服务层进行调用,该服务层返回数据实体或实体列表,或根据场景执行操作业务流程

这是我的困惑。我需要一个有效的策略来记录发生的异常,以便进行故障排除,同时向用户显示友好消息,或者在某些情况下根本不显示

例如,假设服务层中的某些代码导致NullReferenceException,我希望在记录异常以进行故障排除时为用户优雅地处理此问题。此外,假设存储库层中发生异常,例如在尝试访问数据库时发生连接错误。这将是另一个我想以同样方式处理的场景

在处理DDD时,建议采用什么方法来解决这种情况?我有我的存储库->服务层->控制器->用户界面

我目前的方法是创建一个特定于存储库层的异常和一个特定于服务层的异常,存储库层中发生的故障将被引导到服务层,用户界面可以根据自己的判断进行处理

但是,我想利用Azure日志将错误添加到日志文件中,以便进一步调查

处理不同层之间错误的推荐方法是什么? 在这个分层场景中,建议在什么位置添加日志记录? 至少在不使用包装器类的情况下,将azure日志记录放在服务层或存储库层似乎是不好的


是否有一种全球性的方法来处理这一问题,而不必考虑每一个例外?是否有一种全面的方法来处理可能出现的任何例外

这里没有确切的答案,但下面是一个我已经使用过几次的解决方案,效果非常好。不仅针对异常处理,而且针对所有横切关注点

一种可能的方法是使用decorator模式。我写了一篇文章,你可以在这里查看:

我还建议您查看Greg Young关于大致相同主题的视频:

为了使用decorator模式,您可以将返回数据和执行业务流程的方法转换为查询和命令处理程序。假设您有以下方法:

List<Customer> GetCustomers(string country, string orderBy)
{
    ...
}

void CreateInvoice(int customerId, decimal amount)
{
    ...
}

void CreateCustomer(string name, string address)
{
    ...
}
现在,这些方法不符合接口,因此无法提取接口。但是,您可以将它们更改为查询和命令模式:

接口: 接口处理程序 { TResult handlequery查询; }

现在,您可以更改类,以便它们实现此接口:

class GetCustomersHandler : IQueryHandler<CustomerQuery, List<Customer>>
{
    List<Customer> Handle(CustomerQuery query)
    {
        // CustomerQuery is a simple message type class which contains country and orderby
        // just as in the original method, but now packed up in a 'message'
    }
}

class CreateInvoiceHandler : ICommandHandler<CreateInvoice>
{
    public void Handle(CreateInvoice command)
    {
        // CreateInvoice is a simple message type class which contains customerId and amount
        // just as in the original method, but now packed up in a 'message'
    }
}
有了此功能后,您可以创建一个logger类,该类实现了日志记录,但包装装饰了底层类:

class QueryExceptionHandler<TQuery, TResult> : IQueryHandler<TQuery, TResult>
{
    IQueryHandler<TQuery, TResult> _innerQueryHandler;
    public QueryLogHandler(IQueryHandler<TQuery, TResult> innerQueryHandler)
    {
        _innerQueryHandler = innerQueryHandler;
    }

    TResult Handle(TQuery query)
    {
         try
         {
             var result = _innerQueryHandler.Handle(query);
         }
         catch(Exception ex)
         {
              // Deal with exception here
         }
    }
}
当您想要使用它时,您可以从UI代码中像这样实例化它

IQueryHandler<CustomerQuery, List<Customer>> handler = 
    new QueryExceptionHandler<CustomerQuery, List<Customer>>(new GetCustomersHandler());

var customers = handler.Handle(new CustomerQuery {Country = "us", OrderBy = "Name"});
当然,此queryExceptionHandler也可以用于其他处理程序,例如:

IQueryHandler<InvoiceQuery, List<Invoice>> handler = 
    new QueryExceptionHandler<InvoiceQuery, List<Invoice>>(new GetCInvoicesHandler());

var invoices= handler.Handle(new InvoiceQuery {MinAmount= 100});
现在,异常处理是在一个类中完成的,而您的所有其他类都不需要为此烦恼。同样的想法也可以应用到业务操作命令端

除此之外,在本例中,我只添加了一层用于异常处理。您还可以将异常处理程序包装在记录器中,以便在彼此之上构建各种装饰器。这样,您就可以创建一个用于日志记录的类,一个用于异常处理的类,一个用于

它不仅允许您将该行为从实际类中分离出来,而且还允许您为每个不同的处理程序自定义该行为,例如,如果您愿意,可以将带有异常和日志记录的客户处理程序和仅在日志记录处理程序中的发票处理程序包装起来

构建上面示例中的处理程序非常麻烦,特别是当您开始添加多个decorator时,但这只是为了向您展示它们是如何协同工作的

使用依赖注入是一个更好的主意。您可以使用手动DI,这是一种功能性方法,可以查看Greg Young的视频,也可以使用DI容器


我知道这看起来像是一个非常复杂的示例,但您很快就会注意到,一旦您设置了小结构,它实际上非常容易使用。您可以参考我的文章,其中还可以看到使用DI容器的解决方案。

我不确定是否喜欢每次使用命令或查询处理程序时调用异常处理程序。还是换个名字好吗+1.您不需要调用ExceptionHandler。从消费者的角度来看,它只是一个IQueryHandler。特定实例将是一个exceptionhandler,围绕着一个GetCustomerHandler。您通常在一个组合根目录中处理这个问题,如果您正在使用一个DI容器,则在一个DI容器中处理
IQueryHandler<InvoiceQuery, List<Invoice>> handler = 
    new QueryExceptionHandler<InvoiceQuery, List<Invoice>>(new GetCInvoicesHandler());

var invoices= handler.Handle(new InvoiceQuery {MinAmount= 100});