web-dev-qa-db-fra.com

IServiceProvider dans ASP.NET Core

Je commence à apprendre les modifications dans ASP.NET 5(vNext) Je ne trouve pas comment obtenir IServiceProvider, par exemple dans la méthode "Model"

public class Entity 
{
     public void DoSomething()
     { 
           var dbContext = ServiceContainer.GetService<DataContext>(); //Where is ServiceContainer or something like that ?
     }
}

Je sais, nous configurons des services au démarrage, mais où toute la collection de services restant ou IServiceProvider?

27
Sam

Vous devez importer un espace de noms Microsoft.Extensions.DependencyInjection pour accéder au générique.

GetService<T>();

méthode d'extension qui devrait être utilisé sur 

IServiceProvider 

Notez également que vous pouvez directement injecter des services dans les contrôleurs sous ASP.NET 5. Voir l'exemple ci-dessous.

public interface ISomeService
{
    string ServiceValue { get; set; }
}

public class ServiceImplementation : ISomeService
{
    public ServiceImplementation()
    {
        ServiceValue = "Injected from Startup";
    }

    public string ServiceValue { get; set; }
}

Startup.cs

public void ConfigureService(IServiceCollection services)
{
    ...
    services.AddSingleton<ISomeService, ServiceImplementation>();
}

AccueilContrôleur

using Microsoft.Extensions.DependencyInjection;
...
public IServiceProvider Provider { get; set; }
public ISomeService InjectedService { get; set; }

public HomeController(IServiceProvider provider, ISomeService injectedService)
{
    Provider = provider;
    InjectedService = Provider.GetService<ISomeService>();
}

L'une ou l'autre approche peut être utilisée pour accéder au service. Extensions de service supplémentaires pour Startup.cs

AddInstance<IService>(new Service())

Une seule instance est donnée tout le temps. Vous êtes responsable de la création initiale de l'objet.

AddSingleton<IService, Service>()

Une seule instance est créée et agit comme un singleton.

AddTransient<IService, Service>()

Une nouvelle instance est créée chaque fois qu'elle est injectée.

AddScoped<IService, Service>()

Une seule instance est créée dans la portée de la requête HTTP actuelle. C'est l'équivalent de Singleton dans le contexte actuel.

Mis à jour le 18 octobre 2018 

Voir: aspnet GitHub - ServiceCollectionServiceExtensions.cs

46
Jaime Still

Je ne pense pas que ce soit une bonne idée pour une entité (ou un modèle) d'avoir accès à un service. 

En revanche, les contrôleurs ont accès à n’importe quel service enregistré dans leurs constructeurs et vous n’avez pas à vous en préoccuper.

public class NotifyController : Controller
{
    private static IEmailSender emailSender = null;
    protected static ISessionService session = null;
    protected static IMyContext dbContext = null;
    protected static IHostingEnvironment hostingEnvironment = null;

    public NotifyController(
                IEmailSender mailSenderService,
                IMyContext context,
                IHostingEnvironment env,
                ISessionService sessionContext)
    {
        emailSender = mailSenderService;
        dbContext = context;
        hostingEnvironment = env;
        session = sessionContext;
    }
}
4
Leonardo Herrera

Généralement, vous voulez que la DI fasse son travail et l'injecte pour vous:

public class Entity 
{
    private readonly IDataContext dbContext;

    // The DI will auto inject this for you
    public class Entity(IDataContext dbContext)
    {
        this.dbContext = dbContext;
    }

     public void DoSomething()
     {
         // dbContext is already populated for you
         var something = dbContext.Somethings.First();
     }
}

Cependant, Entity devrait être automatiquement instancié pour vous ... comme une Controller ou une ViewComponent. Si vous devez instancier manuellement ceci à partir d'un endroit où cette dbContext n'est pas disponible, vous pouvez le faire:

using Microsoft.Extensions.PlatformAbstractions;

public class Entity 
{
    private readonly IDataContext dbContext;

    public class Entity()
    {
        this.dbContext = (IDataContext)CallContextServiceLocator.Locator.ServiceProvider
                            .GetService(typeof(IDataContext));
    }

     public void DoSomething()
     {
         var something = dbContext.Somethings.First();
     }
}

Mais juste pour souligner, ceci est considéré comme un anti-modèle et devrait être évité sauf si absolument nécessaire. Et… au risque de déranger certaines personnes du modèle… si tout le reste échoue, vous pouvez ajouter un static IContainer dans une classe auxiliaire ou quelque chose de ce type et l'assigner dans votre classe StartUp dans la méthode ConfigureServices: MyHelper.DIContainer = builder.Build(); Et c'est un vraiment laide façon de le faire, mais parfois vous avez juste besoin de le faire fonctionner.

2
Serj Sagan

Au lieu de mettre votre service en ligne, essayez de l'injecter dans le constructeur.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient(typeof(DataContext));
    }
}

public class Entity
{
    private DataContext _context;

    public Entity(DataContext context)
    {
        _context = context;
    }

    public void DoSomething()
    {
        // use _context here
    }
}

Je suggère également de lire ce que signifie AddTransient, car cela aura un impact significatif sur la manière dont votre application partage les instances de DbContext. C'est un modèle appelé Injection de dépendance . Il faut un certain temps pour s'y habituer, mais vous ne voudrez plus jamais y retourner une fois.

1
Anton

Je pense que le PO devient confus. Les entités doivent être aussi minces que possible. Ils doivent essayer de ne pas contenir de logique ni de références externes autres que les propriétés de navigation. Recherchez des modèles courants, tels que les modèles de référentiel, qui permettent d'abstraire votre logique des entités elles-mêmes.

0
Robert Perry