2013-04-29 11 views
6

Mam klasa, która zwraca się do serwisu internetowego, aby uzyskać pewne dane:programowo zmienić zależność w zamku Windsor

public class MarketingService 
{ 
    private IDataProvider _provider; 
    public MarketingService(IDataProvider provider) 
    { 
     _provider = provider; 
    } 

    public string GetData(int id) 
    { 
     return _provider.Get(id); 
    } 
} 

Obecnie mam dwóch dostawców: HttpDataProvider i FileDataProvider. Zazwyczaj podłączę się do HttpDataProvider, ale jeśli zewnętrzna usługa internetowa zawiedzie, chciałbym zmienić system tak, aby wiązał się z FileDataProvider. Coś jak:

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; 
} 

Więc kiedy ten został zawartej wszystkich przyszłych wystąpień MarketingService zostanie automatycznie podłączony do FileDataProvider. Czy ktoś wie, jak zmienić wiązanie Windsor w locie?

Odpowiedz

6

Jednym rozwiązaniem byłoby przy użyciu Selektor

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]; 
    } 
} 

test i korzystanie

[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()); 
    } 
} 
+0

Korzystaliśmy z tej implementacji z powodzeniem, jednak nasze najbardziej zajmujące wątki działały ze słownikiem, więc zmieniliśmy ją na ConcurrentDictionary, aby była bezpieczna dla wątków zgodnie z zaleceniami tutaj: https://blogs.msdn.microsoft.com/tess/2009/12/21/high-cpu-in-net-app-using-a-static-generic-dictionary / – Calum

0

Alternatywnie można utworzyć proxy:

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>()); 

to zawsze najpierw postarać się o dane z HttpDataProvider, jeśli nie powiodło się, użyje kreacji zastępczej. Jeśli chcesz możesz wprowadzić stan i po awarii zawsze używaj awaryjnego. W ten sposób możesz nadal korzystać z IDataProvider w aplikacji bez konieczności uzyskiwania nowego z kontenera.

Powiązane problemy