Il modo migliore per utilizzare StructureMap per implementare il modello di strategia

La mia app web presenta alcune lievi variazioni nella logica di business e nella logica di presentazione a seconda del tipo di utente che ha effettuato l’accesso. Sembra che ottenere variazioni mediante l’iniezione di diverse classi concrete basate sul tipo di utente sia una buona soluzione per DI. Quindi mi chiedo quali funzioni di StructureMap I dovrei usare per ottenere questo risultato (o se sono fuori dagli schemi di base).

(Ho appena appreso che i profili non sono il modo per farlo perché l’impostazione del profilo non è un’operazione per thread: i profili StructureMap sono thread-safe? )

MODIFICARE

È questa la strada da percorrere per questo?

public class HomeController { private ISomeDependancy _someDependancy; public HomeController(ISomeDependancy someDependancy) { _someDependancy = someDependancy; } public string GetNameFromDependancy() { return _someDependancy.GetName(); } } public interface ISomeDependancy { string GetName(); } public class VersionASomeDependancy : ISomeDependancy { public string GetName() { return "My Name is Version A"; } } public class VersionBSomeDependancy : ISomeDependancy { public string GetName() { return "My Name is Version B"; } } public class VersionARegistry : Registry { public VersionARegistry() { // build up complex graph here ForRequestedType().TheDefaultIsConcreteType(); } } public class VersionBRegistry : Registry { public VersionBRegistry() { // build up complex graph here ForRequestedType().TheDefaultIsConcreteType(); } } public class ContainerA : Container { public ContainerA() : base(new VersionARegistry()) { } } public class ContainerB : Container { public ContainerB() : base(new VersionBRegistry()) { } } [TestFixture] public class Harness { [Test] public void ensure_that_versions_load_based_on_named_containers() { ObjectFactory.Initialize(c => { c.ForRequestedType().AddInstances( x => { x.OfConcreteType().WithName("VersionA"); x.OfConcreteType().WithName("VersionB"); }).CacheBy(InstanceScope.Singleton); }); HomeController controller; IContainer containerForVersionA = ObjectFactory.GetNamedInstance("VersionA"); controller = containerForVersionA.GetInstance(); Assert.That(controller.GetNameFromDependancy(), Is.EqualTo("My Name is Version A")); IContainer containerForVersionB = ObjectFactory.GetNamedInstance("VersionB"); controller = containerForVersionB.GetInstance(); Assert.That(controller.GetNameFromDependancy(), Is.EqualTo("My Name is Version B")); } } 

    Un modo comune per implementare questo è come descritto da Mark. Si dispone di una class che raccoglie una serie di tutte le istanze concrete ( deve essere una matrice affinché StructureMap si comporti come previsto) e quindi utilizza una logica per determinare quale istanza utilizzare.

    Alcuni esempi di codice che puoi incollare in un programma di console o in un test di unità:

     var container = new Container(x => x.Scan(scan => { scan.TheCallingAssembly(); scan.WithDefaultConventions(); scan.AddAllTypesOf(); })); var strategy = container.GetInstance(); Console.WriteLine(strategy.GetDiscount("Regular", 10)); // 0 Console.WriteLine(strategy.GetDiscount("Normal", 10)); // 1 Console.WriteLine(strategy.GetDiscount("Special", 10)); // 5 

    che dipende dai seguenti tipi:

     public interface IDiscountStrategy { decimal GetDiscount(string userType, decimal orderTotal); } public class DiscountStrategy : IDiscountStrategy { private readonly IDiscountCalculator[] _discountCalculators; public DiscountStrategy(IDiscountCalculator[] discountCalculators) { _discountCalculators = discountCalculators; } public decimal GetDiscount(string userType, decimal orderTotal) { var calculator = _discountCalculators.FirstOrDefault(x => x.AppliesTo(userType)); if (calculator == null) return 0; return calculator.CalculateDiscount(orderTotal); } } public interface IDiscountCalculator { bool AppliesTo(string userType); decimal CalculateDiscount(decimal orderTotal); } public class NormalUserDiscountCalculator : IDiscountCalculator { public bool AppliesTo(string userType) { return userType == "Normal"; } public decimal CalculateDiscount(decimal orderTotal) { return orderTotal * 0.1m; } } public class SpecialUserDiscountCalculator : IDiscountCalculator { public bool AppliesTo(string userType) { return userType == "Special"; } public decimal CalculateDiscount(decimal orderTotal) { return orderTotal * 0.5m; } } 

    Direi che questo non è lo scopo principale di DI – cioè colbind e iniettare dipendenze, qualunque esse siano. Nessuna logica applicativa dovrebbe essere coinvolta nel cablaggio dei componenti – dovrebbe basarsi strettamente sulla configurazione; tramite codice o file .config. Quella configurazione è a livello di applicazione, quindi è abbastanza difficile definire una configurazione che varia a seconda dell’utente.

    Detto questo, quello che stai chiedendo va benissimo di pari passo con il DI – è solo un po ‘perpendicolare a DI in sé.

    Per il tuo scopo specifico, definirei una nuova dipendenza sotto forma di un’interfaccia o di una class base astratta. Questa sarebbe una strategia che seleziona i tipi concreti corretti (quelli che vuoi variare) in base all’utente corrente.

    È ansible utilizzare DI per iniettare tutti i tipi di calcestruzzo disponibili in questa strategia, che avrebbe quindi un metodo o una proprietà che restituisce la scelta corretta tra i servizi iniettati, in base all’utente corrente.

    In tutti i luoghi in cui sei solito dipendere dai vari servizi per utente, rimuovi quelle vecchie dipendenze e le sostituisci con una dipendenza dalla Strategia.