如何设置Castle.WindsorContainer不要在解析时包装已知异常

时间:2015-04-20 09:18:57

标签: c# exception-handling castle-windsor

我使用castle通过构造函数注入来解决依赖关系。 我的问题是服务的构造函数可能使用第三方插件代码。 如果此插件代码抛出异常,它将包含在已知异常中,例如“KnownException”。

我有一个特殊的异常策略来记录和处理“KnownException”。 问题是,如果Castle Windsorcontainer解决方案遇到异常,它将被包装在'Castle.MicroKernel.ComponentActivator.ComponentActivatorException'中。

有没有办法避免这种情况,只是让'KnownException'传播?

一个简单的场景:

public class KnownException : Exception
{

}

// registerd in castle
public class DetailViewModel
{
    public DetailViewModel(Selector thirdPartySelector)
    {
        thirdPartySelector.GetElements(); // might throw known exception
    }
}

// registered as TypedFactory in Castle
public interface IViewModelFactory
{

    DetailViewModel Create(Selector thirdPartySelector);
}


public class MasterViewModel
{
    private readonly IViewModelFactory viewModelFactory;
    private readonly Selector selector;

    public MasterViewModel(IViewModelFactory viewModelFactory)
    {
        this.viewModelFactory = viewModelFactory;
        selector = Mock.Of<Selector>();  // for illustration purpose only. Creation is more elaborate, and not relly a problem here.
    }


    public void OnAddDetailed()
    {
        try
        {
            var vm = viewModelFactory.Create(selector);

            // adding detailed view model.

        }
        catch (KnownException e)
        {
            // Log and continue
        }
    }
}

}

3 个答案:

答案 0 :(得分:0)

显然,当Castle尝试解析组件时,ComponentActivatorException会在抛出之前包装初始异常。没有办法绕过这种行为。

您最好的选择是抓住ComponentActivatorException并抛出InnerException以便再次找到已知的错误处理类型

答案 1 :(得分:0)

为避免城堡使用组件激活器,您可以使用UsingFactoryMethod注册组件,即:

Component.For<IFoo>().UsingFactoryMethod(k => new Foo())

有一些缺点: 如果Foo具有依赖关系,则需要自己从容器中解析这些依赖关系,并且拦截器将无法工作。如果需要,OnDestroy可以帮助释放依赖项。

答案 2 :(得分:0)

我通过装饰工厂解决了这个问题,捕获了ComponentActivatorException并抛出了InnerException。 但我想知道,如果这应该由集装箱解决,就像我使用工厂一样,我不应该知道外面有一个集装箱。但是如果create方法抛出CastleWindsor异常,则情况并非如此。

我的解决方案:

[TestMethod]
public void CastleWindsor_FactoryThrows_MyException()
{
    WindsorContainer Container = new WindsorContainer();

    Container.AddFacility<TypedFactoryFacility>();

    Container.Register(Component.For<IMyFactory>().ImplementedBy<MyFactoryDecorator>());
    Container.Register(Component.For<IMyFactory>().AsFactory());
    Container.Register(Component.For<MyClass>().LifestyleTransient());

    var factory = Container.Resolve<IMyFactory>();

    try
    {
        factory.Create();
    }
    catch (Exception e)
    {
        Assert.AreEqual(e.GetType(), typeof(MyException));
    }
}

public class MyFactoryDecorator : IMyFactory
{
    IMyFactory factory;

    public MyFactoryDecorator(IMyFactory aFactory)
    {
        factory = aFactory;
    }

    MyClass IMyFactory.Create() => handleComponentActivatorException(() => factory.Create());


    T handleComponentActivatorException<T>(Func<T> action)
    {
        try
        {
            return action();
        }
        catch (ComponentActivatorException e)
        {
            throw e.InnerException.InnerException;
        }
    }
}

public interface IMyFactory
{
    MyClass Create();
}

public class MyException : Exception { }

public class MyClass
{
    public MyClass()
    {
        throw new MyException();
    }
}