Supposons que j'ai le Service
qui reçoit les dépendances via le constructeur mais doit également être initialisé avec des données personnalisées (contexte) avant de pouvoir être utilisé:
public interface IService
{
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
}
public class Service : IService
{
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
{
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
}
public void Initialize(Context context)
{
// Initialize state based on context
// Heavy, long running operation
}
public void DoSomething()
{
// ...
}
public void DoOtherThing()
{
// ...
}
}
public class Context
{
public int Value1;
public string Value2;
public string Value3;
}
Maintenant - les données de contexte ne sont pas connues à l'avance, donc je ne peux pas l'enregistrer en tant que dépendance et utiliser DI pour l'injecter dans le service
Voici à quoi ressemble un exemple de client:
public class Client
{
private readonly IService service;
public Client(IService service)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
}
public void OnStartup()
{
service.Initialize(new Context
{
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
});
}
public void Execute()
{
service.DoSomething();
service.DoOtherThing();
}
}
Comme vous pouvez le voir - il y a un couplage temporel et initialise les odeurs de code de méthode impliquées, car je dois d'abord appeler service.Initialize
pour pouvoir appeler service.DoSomething
et service.DoOtherThing
ensuite.
Quelles sont les autres approches dans lesquelles je peux éliminer ces problèmes?
Clarification supplémentaire du comportement:
Chaque instance du client doit avoir sa propre instance du service initialisée avec les données de contexte spécifiques du client. Ainsi, ces données de contexte ne sont pas statiques ou connues à l'avance, elles ne peuvent donc pas être injectées par DI dans le constructeur.
Service
a des dépendances autres que leContext
, qui ne seraient pas fournies par leClient
, elles peuvent être fournies via DI auServiceFactory
pour être transmises auService
quandcreateService
est appelé.ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);
et vous retrouver avec votre service partiellement configuré, puis le faire plus tardService s = partial.context(context).build()
La
Initialize
méthode doit être supprimée de l'IService
interface, car il s'agit d'un détail d'implémentation. À la place, définissez une autre classe qui prend l'instance concrète de Service et appelle la méthode initialize dessus. Ensuite, cette nouvelle classe implémente l'interface IService:Cela permet au code client d'ignorer la procédure d'initialisation, sauf lorsque la
ContextDependentService
classe est initialisée. Vous limitez au moins les parties de votre application qui ont besoin de connaître cette procédure d'initialisation bancale.la source
Il me semble que vous avez deux options ici
par exemple.
par exemple.
Injecter une fabrique est très bien si vous voulez juste éviter de passer le contexte en paramètre. Supposons que cette implémentation particulière ait besoin d'un contexte et que vous ne souhaitiez pas l'ajouter à l'interface
Mais vous avez essentiellement le même problème, et si l'usine n'a pas encore de contexte initialisé.
la source
Vous ne devez pas dépendre votre interface de n'importe quel contexte db et initialiser la méthode. Vous pouvez le faire dans un constructeur de classe concret.
Et, une réponse à votre question principale serait l' injection de propriété .
De cette façon, vous pouvez appeler toutes les dépendances par injection de propriété . Mais cela pourrait être énorme. Si c'est le cas, vous pouvez utiliser l'injection de constructeur pour eux, mais vous pouvez définir votre contexte par propriété en vérifiant s'il est nul.
la source
Misko Hevery a un article de blog très utile sur l'affaire que vous avez rencontrée. Vous avez tous les deux besoin de produits nouveaux et injectables pour votre
Service
classe et ce billet de blog peut vous aider.la source