2016-09-21 1 views
1

J'ai un projet web api où les contrôleurs dépendent d'une couche de stockage. Par exemple. chaque contrôleur a un code similaire:Castle Windsor - Pass résoudre les arguments aux couches inférieures

public class ResourceController : ApiController 
{ 
    private readonly IResourceStorage _resourceStorage; 
    private readonly IOtherStorage _otherStorage; 


    public ResourceController(IResourceStorage resourceStorage, IOtherStorage otherStorage) 
    { 
     _resourceStorage = resourceStorage; 
     _otherStorage = otherStorage; 
    } 

    // Web API Actions 
} 

Un code commun pour le stockage ressemble à ceci:

public class ResourceStorage : DBProvider, IResourceStorage 
{ 
    public ResourceStorage(string connectionString) : base(connectionString) 
    { 
    } 

    // storage methods 
} 

Sur la base d'une condition spécifique de la demande Api Web, je dois pouvoir injecter différents connectionStrings aux stockages du contrôleur. pseudo-code pourrait ressembler à ça:

public class WindsorControllerActivator : IHttpControllerActivator 
{ 
    public IHttpController Create(
     HttpRequestMessage request, 
     HttpControllerDescriptor controllerDescriptor, 
     Type controllerType) 
    { 
     string connectionString = ChooseDbConnectionStringBasedOnRequestContext(request); 

     var controller = 
     (IHttpController)_container.Resolve(connectionString, controllerType, new {databaseType = connectionString}); 

     request.RegisterForDispose(
     new Release(
      () => _container.Release(controller))); 

     return controller; 
    } 
} 

Je ne sais pas comment passer le paramètre au stockage de la manière moins destructrice :)

Qu'est-ce que j'essayé? Alternatives:

  1. parameterers Pass le château de Windsor Arguments supplémentaires et le traiter dans la prochaine couche à l'aide DynamicParameters. Les arguments obtiennent la couche de contrôleur mais je n'ai pas pu trouver un moyen de les amener au stockage - il a son propre CreationContext, et je ne peux pas trouver un moyen de le transmettre du contrôleur au stockage.
  2. Possédez N (nombre de chaînes de connexion égal) et choisissez l'un des conteneurs ControllerActivator. Semble une solution énorme et laide, totalement non-flexible mais cela fonctionne.
  3. Créez N ensembles de contrôleurs chacun avec leur propre nom et à l'intérieur du stockage DynamicParameters Vérifiez le nom du composant Handler et choisissez la chaîne de connexion. De ControllerActivator passer dans la clé à l'ensemble correct de contrôleurs. Aussi moche - trop d'enregistrements de contrôleurs et beaucoup de code de plomberie.

Répondre

1

Vous pouvez utiliser un modèle d'usine:

public interface IResourceStorageFactory 
{ 
    IResourceStorage Create(int numberOfResources); 
} 

public class ResourceStorageFactory : IResourceStorageFactory 
{ 
    public IResourceStorage Create(HttpRequestMessage request) 
    { 
     var connectionString = ChooseDbConnectionStringBasedOnRequestContext(request); 

     return new ResourceStorage(connectionString); 
    } 
} 

puis simplement

private readonly IResourceStorage _resourceStorage; 
private readonly IOtherStorage _otherStorage; 

public ResourceController(IResourceStorageFactory resourceStorageFactory, IOtherStorage otherStorage) 
{ 
    _resourceStorage = resourceStorageFactory.Create(Request); 
    _otherStorage = otherStorage; 
} 
+0

C'est beaucoup moins intrusif, c'est sûr. Merci pour la suggestion.Encore il y a 2 problèmes que je vois ici: 1) On doit fournir des paramètres supplémentaires StorageFactory à tous les contrôleurs (il peut être rendu générique, mais encore un paramètre supplémentaire), 2) On doit appeler créer avant d'assigner chaque stockage qui est aussi plutôt intrusif. Je ne marquerai pas votre réponse comme étant encore acceptée et j'espère que quelqu'un aura une solution plus naturelle pour cela –

+0

Par exemple - l'usine pourrait-elle être utilisée pour injecter un certain type d'injection IRegistration? –

0

La solution que je trouve est l'introduction d'un fournisseur de chaîne de connexion:

public interface IConnectionStringProvider 
{ 
    string ConnectionString { get; } 
} 

Quand inscrire ce fournisseur selon la demande Web avec une méthode de fabrication:

kernel.Register(
    Component.For(typeof (IConnectionStringProvider)) 
      .ImplementedBy(typeof (ConnectionStringProvider)) 
      .UsingFactoryMethod(
       (k, context) => 
        new ConnectionStringProvider(context.AdditionalArguments["connectionString"].ToString())) 
      .LifestylePerWebRequest()); 

Et à l'intérieur de l'activateur du premier contrôleur de résoudre le fournisseur de chaîne de connexion avec les bons paramètres et le régulateur:

// The lifecycle of connection string provider if per web request. 
// We resolve it first time with the correct parameters, 
// so it is injected with the correct connection string throught the lifecycle of the request. 
_container.Resolve<IConnectionStringProvider>(new {connectionString}); 

var controller = 
    (IHttpController) _container.Resolve(controllerType); 

Il est pas encore parfait et ressemble à un peu hackish mais il permet de garder des dépendances sur les interfaces de la couche inférieure et nécessite moins de changements de la base de code des solutions que j'ai trouvées