use*_*300 2 c# domain-driven-design exception azure
我正在使用领域驱动设计开发 MVC 5 Web 应用程序。我的控制器基本上调用服务层,该服务层根据场景返回数据(实体或实体列表)或执行操作(业务流程)。
这是我的困惑。我需要一种有效的策略来记录出于故障排除目的而发生的异常,同时向用户显示友好的消息或在某些条件下根本不显示。
例如,假设服务层中的某些代码导致 NullReferenceException,我想在记录异常以进行故障排除时为用户优雅地处理此问题。此外,假设存储库层发生异常,例如尝试访问数据库时出现连接错误。这将是我想以相同方式处理的另一种情况。
当您处理 DDD 时,针对这种情况的推荐方法是什么?我有我的存储库 -> 服务层 -> 控制器 -> UI。
我当前的方法是创建一个特定于存储库层的异常和一个特定于服务层的异常,存储库层中发生的故障将向上冒泡到服务层,UI 可以根据其判断进行处理。
但是,我想利用 Azure 日志记录将错误添加到日志文件中以供进一步调查。
将 azure 日志记录放在服务或存储库层中似乎很糟糕,至少不使用包装类?
是否有一种全局方法来处理这个问题,而不必考虑每个异常(捕获所有可能漏掉的异常)。
这里并没有真正明确的答案,但以下是我已经使用过几次并且效果很好的解决方案。(不仅用于异常处理,还用于所有横切问题)。
一种可能的方法是使用装饰器模式。我写了一篇关于此的文章,您可以在这里查看:http://www.kenneth-truyers.net/2014/06/02/simplify-code-by-using-composition/
我还建议您查看 Greg Young 的视频,内容大致相同:http://www.infoq.com/presentations/8-lines-code-refactoring
为了使用装饰器模式,您可以将返回数据和执行业务流程的方法转换为查询和命令处理程序。假设您有以下方法:
List<Customer> GetCustomers(string country, string orderBy)
{
...
}
void CreateInvoice(int customerId, decimal amount)
{
...
}
void CreateCustomer(string name, string address)
{
...
}
Run Code Online (Sandbox Code Playgroud)
现在,这些方法不符合接口,因此您无法提取它们。但是,您可以将它们更改为查询和命令模式:
接口: 接口 IQueryHandler { TResult Handle(TQuery query); }
interface ICommandHandler<T>
{
Handle(T command);
}
Run Code Online (Sandbox Code Playgroud)
现在您可以更改您的类,以便它们实现此接口:
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'
}
}
Run Code Online (Sandbox Code Playgroud)
当你有了这个,你可以创建一个记录器类来实现日志记录但包装(装饰)底层类:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
当你想使用它时,你可以像这样实例化它(从 UI 代码)。
IQueryHandler<CustomerQuery, List<Customer>> handler =
new QueryExceptionHandler<CustomerQuery, List<Customer>>(new GetCustomersHandler());
var customers = handler.Handle(new CustomerQuery {Country = "us", OrderBy = "Name"});
Run Code Online (Sandbox Code Playgroud)
当然,这个 queryExceptionHandler 也可以重用于其他处理程序(示例):
IQueryHandler<InvoiceQuery, List<Invoice>> handler =
new QueryExceptionHandler<InvoiceQuery, List<Invoice>>(new GetCInvoicesHandler());
var invoices= handler.Handle(new InvoiceQuery {MinAmount= 100});
Run Code Online (Sandbox Code Playgroud)
现在,异常处理是在一个类中完成的,所有其他类都不需要担心它。相同的想法可以应用于业务操作(命令端)。
除此之外,在本例中我只是添加了一层用于异常处理。您也可以将异常处理程序包装在记录器内,从而在彼此之上构建各种装饰器。这样您就可以创建一个用于日志记录的类,一个用于异常处理,一个用于...
它不仅允许您将该行为与实际的类分开,而且还允许您根据需要为每个不同的处理程序自定义它(例如,仅在日志处理程序中包装带有异常和日志记录的客户处理程序和发票处理程序) )
像上面的示例一样构建处理程序非常麻烦(特别是当您开始添加多个装饰器时),但这只是为了向您展示它们如何协同工作。
为此使用依赖注入将是一个更好的主意。您可以进行手动 DI,这是一种功能性方法(请参阅 Greg Young 的视频)或使用 DI 容器。
我知道它看起来是一个非常复杂的示例,但您很快就会注意到,一旦设置了这个小结构,它实际上很容易使用。您可以参考我的文章,您还可以看到使用 DI 容器的解决方案。
| 归档时间: |
|
| 查看次数: |
2087 次 |
| 最近记录: |