通过编程方式更改Castle Windsor中的依赖项

本文关键字:依赖 Windsor Castle 编程 方式更 | 更新日期: 2023-09-27 18:11:18

我有一个类,它调用一个互联网服务来获取一些数据:

public class MarketingService
{
    private IDataProvider _provider;
    public MarketingService(IDataProvider provider)
    {
        _provider = provider;
    }
    public string GetData(int id)
    {
        return _provider.Get(id);
    }
}

目前我有两个提供者:HttpDataProvider和FileDataProvider。通常我会连接到HttpDataProvider,但如果外部web服务失败,我想更改系统绑定到fileddataprovider。比如:

public string GetData(int id)
{
    string result = "";
    try
    {
        result = GetData(id); // call to HttpDataProvider
    }
    catch (Exception)
    {
        // change the Windsor binding so that all future calls go automatically to the
        // FileDataProvier
        // And while I'm at it, retry against the FileDataProvider    
    }
    return result;
}

因此,当执行此操作时,所有未来的MarketingService实例将自动连接到FileDataProvider。如何在飞行中改变温莎装订?

通过编程方式更改Castle Windsor中的依赖项

一个解决方案是使用选择器

public class ForcedImplementationSelector<TService> : IHandlerSelector
{
    private static Dictionary<Type, Type>  _forcedImplementation = new Dictionary<Type, Type>();
    public static void ForceTo<T>() where T: TService
    {
        _forcedImplementation[typeof(TService)] = typeof(T);
    }
    public static void ClearForce()
    {
        _forcedImplementation[typeof(TService)] = null;
    }
    public bool HasOpinionAbout(string key, Type service)
    {
        return service == typeof (TService);
    }
    public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
    {
        var tService = typeof(TService);
        if (_forcedImplementation.ContainsKey(tService) && _forcedImplementation[tService] != null)
        {
            return handlers.FirstOrDefault(handler => handler.ComponentModel.Implementation == _forcedImplementation[tService]);
        }
        // return default
        return handlers[0];
    }
}

测试和使用

[TestFixture]
public class Test
{
    [Test]
    public void ForceImplementation()
    {
        var container = new WindsorContainer();
        container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
        container.Register(Component.For<IFoo>().ImplementedBy<Bar>());
        container.Kernel.AddHandlerSelector(new ForcedImplementationSelector<IFoo>());
        var i = container.Resolve<IFoo>();
        Assert.AreEqual(typeof(Foo), i.GetType());
        ForcedImplementationSelector<IFoo>.ForceTo<Bar>();
        i = container.Resolve<IFoo>();
        Assert.AreEqual(typeof(Bar), i.GetType());

        ForcedImplementationSelector<IFoo>.ClearForce();
        i = container.Resolve<IFoo>();
        Assert.AreEqual(typeof(Foo), i.GetType());
    }
}

或者您可以创建一个代理:

public class AutoSelectingDataProvider : IDataProvider 
{
    public AutoSelectingDataPovider(HttpDataProvider httpDataProvider, FallBackDataProvider fallBackProvider)
    {
        _httpDataProvider = httpDataProvider;
        _fallBackDataProvider = fallBackDataProvider;
    }

    public string GetData(int id)
    {
        try
        {
            return _httpDataProvider.GetData(id);
        }
        catch (Exception)
        {
            return _fallBackDataProvider.GetData(id);
        }
    return result;
    }
}

container.Register(
    Component.For<HttpDataProvider>(),
    Component.For<FallBackDataProvider>(),
    Component.For<IDataProvider>().ImplementedBy<FallBackDataProvider>());

这将总是首先尝试从HttpDataProvider获取数据,如果不成功,使用回退。如果您愿意,您可以引入状态,并在失败后始终使用回退。这样,您就可以在应用程序中继续使用IDataProvider,而无需从容器中获取新的IDataProvider。