【问题标题】:How to handle constructor exception when using Autofac WcfIntegration使用 Autofac WcfIntegration 时如何处理构造函数异常
【发布时间】:2012-07-16 23:13:56
【问题描述】:

有没有办法处理 WCF 服务的构造函数抛出的异常,当该构造函数接受依赖项时,它是 IoC 容器(在本例中为 AutoFac)对依赖项的实例化导致异常

考虑具有以下构造函数的 WCF 服务:

public InformationService(IInformationRetriever informationRetriever)
{
    _informationRetriever = informationRetriever;
}
//... the service later makes use of the injected InformationRetriever

该服务使用 AutoFac WcfIntegration 和 AutofacWebServiceHostFactory(这恰好是一个 RESTful 服务)。

依赖注册在服务的 global.asax.cs 中,即:

builder.RegisterType<InformationRetriever>()
                .As<IInformationRetriever>()

现在InformationRetriever 实现在其构造函数中执行一些检查,以确保一切就绪,以便能够完成其工作。当它在这个阶段发现问题时,它会抛出异常。

但是,我不希望服务的调用者收到 AutoFac 异常:

An exception was thrown while invoking the constructor ... on type InformationRetriever

实际上我正在尝试测试:

鉴于信息服务正在运行

我调用 GetSomeInformation() 方法

并且无法实例化 InformationRetriever

那么我想返回一个友好的错误信息

并且记录实际的异常

这是我的设计问题,还是有已知的模式可以克服或防止这个问题?

我四处寻找,找不到任何关于此类问题的信息。

【问题讨论】:

    标签: c# inversion-of-control autofac


    【解决方案1】:

    以 DI 风格编写的对象通常会经历两个独立的阶段:组合和执行。组合阶段是您连接依赖关系并执行诸如抛出参数异常之类的事情的地方。您通常希望保持此阶段没有有意义的行为,因为这允许您在系统的配置中发现错误。第二阶段,执行,是您使用第一阶段的输出(依赖项)来完成工作的地方。

    将这两个阶段分开可以消除很多歧义和复杂性。例如,您不要在给割草机充气的同时修剪草坪;这导致这两种活动变得更加复杂(和危险!)

    在这种情况下,InformationRetriever 通过在其构造函数中执行有意义的工作来合并组合和执行阶段。这种混合正是导致您试图避免的问题:一个有意义的业务异常被包装在一个组合异常中。也不清楚如何处理异常,因为顶级调用者是 Autofac 而不是实际要求 InformationRetriever 工作的组件。

    我建议在调用InformationRetriever 时尽量进行验证;这将删除 Autofac 异常并允许InformationService 处理异常情况而无需任何技巧。

    这种方法的一个潜在缺点是验证将在每次调用 InformationRetriever 时进行,而不是在构造函数中进行一次。您有两个选择:1) 让它每次都发生,以绝对确保工作是有效的,或 2) 跟踪您是否已完成检查,只有在您之前没有完成时才进行。

    如果您选择#2,您可以使用装饰器将InformationRetriever 包装在同一接口的验证版本中,从而保持干净:

    public class ValidatingInformationRetriever : IInformationRetriever
    {
        private readonly IInformationRetriever _baseRetriever;
        private bool _validated;
    
        public ValidatingInformationRetriever(IInformationRetriever baseRetriever)
        {
            _baseRetriever = baseRetriever;
        }
    
        public void Foo()
        {
            if(!_validated)
            {
                Validate();
    
                _validated = true;
            }
    
            _baseRetriever.Foo();
        }
    
        private void Validate()
        {
            // ...
        }
    }
    

    您可以像这样使用Autofac's decorator support 注册它:

    builder
        .RegisterType<InformationRetriever>()
        .Named<IInformationRetriever>("base");
    
    builder.RegisterDecorator<IInformationRetriever>(
        (c, inner) => new ValidatingInformationRetriever(inner),
        fromKey: "base");
    

    【讨论】:

    • 布莱恩,感谢您深思熟虑的回复。我完全理解你关于分离组合和执行阶段的观点。我相信在这种特殊情况下,尽管实际上是组合阶段失败了。我认为 InformationRetriever 需要其他东西才能完成它的工作。如果那不存在,那么它什么也做不了。不过,我会仔细研究您提到的选项。
    【解决方案2】:

    我不喜欢构造函数由于错误参数以外的原因抛出异常。我可能会以不同的方式为我的类型建模。但这里有一些想法。起初我想过做这样的事情:

    builder
        .Register(c => {
            try
            {
                return new InformationRetriever();
            }
            catch (Exception)
            {
                return new FailoverInformationRetreiver();
            }})
        .As<IInformationRetriever>();
    

    ...FailoverInformationRetreiver 在成员访问时抛出异常。另一个想法可能是:

    public InformationService(Lazy<IInformationRetriever> informationRetriever)
    {
        _informationRetriever = informationRetriever;
    }
    

    try/catch 围绕InformationService 内部的用法。如果在应用启动时知道InformationRetriever 的可用性,您可以选择另一个选项:

    // inside your container builder:
    if (InformationRetreiver.IsAvailable())
        builder.RegisterType<InformationRetriever>()
               .As<IInformationRetriever>()
    
    // inside InformationService, make the dependency optional
    public InformationService(IInformationRetriever informationRetriever = null)
    {
        _informationRetriever = informationRetriever;
    }
    

    这些想法有帮助吗?

    【讨论】:

    • Jim,感谢您的建议,抱歉回复缓慢(我已经离开几天了)。我会尝试一下(目前更喜欢 Lazy 选项),然后在这里报告我的发现
    • 我可以批准“可选依赖”方法对我有用。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多