web-dev-qa-db-fra.com

Injection de propriété dans Asp.Net Core

J'essaie de porter une application asp.net sur asp.net core. J'ai l'injection de propriété (en utilisant ninject) sur mon implémentation UnitOfWork comme ceci. 

[Inject]
public IOrderRepository OrderRepository { get; set; }
[Inject]
public ICustomerRepository CustomerRepository { get; set; }

Existe-t-il un moyen d’obtenir la même fonctionnalité en utilisant l’ID intégré sur le noyau .net? Est-il également possible d'utiliser une liaison basée sur des conventions?

21
Özgür Kaplan

Non, le conteneur DI/IoC intégré est délibérément gardé simple, tant par son utilisation que par ses fonctionnalités, pour offrir une base pour le branchement d'autres conteneurs DI. 

Il n'y a donc pas de prise en charge intégrée pour: Détection automatique, Inscriptions automatiques, Décorateurs ou Injecteurs, ou inscriptions basées sur des conventions. Pour autant que je sache, il n’est pas prévu d’ajouter cela au conteneur intégré.

Vous devrez utiliser un conteneur tiers avec un support d'injection de propriété. 

Veuillez noter que l'injection de propriété est considérée comme mauvaise dans 98% des scénarios, car elle masque les dépendances et rien ne garantit que l'objet sera injecté lors de la création de la classe. 

Avec l'injection de constructeur, vous pouvez appliquer ceci via le constructeur et vérifier la valeur null et ne pas créer l'instance de la classe. Avec l'injection de propriété, c'est impossible et lors des tests unitaires, les services/dépendances requis par la classe quand ils ne sont pas définis dans le constructeur ne sont pas évidents. Il est donc facile de manquer et d'obtenir NullReferenceExceptions

La seule raison valable pour l’injection de propriété que j’ai jamais trouvée était d’injecter des services dans des classes de proxy générées par une bibliothèque tierce, c’est-à-dire des proxy WCF créés à partir d’une interface dans laquelle vous n’aviez aucun contrôle sur la création de l’objet. 

Evitez-le partout else.

28
Tseng

Existe-t-il un moyen d’obtenir la même fonctionnalité en utilisant l’ID intégré sur le noyau .net?

Non, mais voici comment vous pouvez créer vos propres attributs [inject] à l'aide du mécanisme d'injection de propriété de autofac .

Commencez par créer votre propre InjectAttribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InjectAttribute : Attribute
{
  public InjectAttribute() : base() { }
}

Créez ensuite votre propre InjectPropertySelector qui utilise la réflexion pour rechercher les propriétés marquées avec [inject]:

public class InjectPropertySelector : DefaultPropertySelector
{
  public InjectPropertySelector(bool preserveSetValues) : base(preserveSetValues)
  { }

  public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
  {
    var attr = propertyInfo.GetCustomAttribute<InjectAttribute>(inherit: true);
    return attr != null && propertyInfo.CanWrite
            && (!PreserveSetValues
            || (propertyInfo.CanRead && propertyInfo.GetValue(instance, null) == null));
  }
}

Utilisez ensuite votre sélecteur dans votre ConfigureServicesoù vous câblez votre AutofacServiceProvider:

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    var builder = new ContainerBuilder();
    builder.Populate(services);

    // use your property selector to discover the properties marked with [inject]
    builder.RegisterType<MyServiceX>().PropertiesAutowired((new InjectablePropertySelector(true)););

    this.ApplicationContainer = builder.Build();
    return new AutofacServiceProvider(this.ApplicationContainer);
  }
}

Enfin, dans votre service, vous pouvez maintenant utiliser [inject]:

public class MyServiceX 
{
    [Inject]
    public IOrderRepository OrderRepository { get; set; }
    [Inject]
    public ICustomerRepository CustomerRepository { get; set; }
}

Vous pouvez sûrement aller encore plus loin avec cette solution, par exemple. en utilisant un attribut pour spécifier le cycle de vie de votre service au-dessus de la définition de classe de votre service ...

[Injectable(LifetimeScope.SingleInstance)]
public class IOrderRepository

... puis recherchez cet attribut lors de la configuration de vos services via autofac. Mais cela irait au-delà de la portée de cette réponse.

1
B12Toaster