web-dev-qa-db-fra.com

Comment créer une injection de dépendance pour ASP.NET MVC 5?

Créer une injection de dépendance avec ASP.NET Core est assez facile. La documentation explique très bien ici et ce gars a une vidéo tueur pour l'expliquer.

Cependant, je souhaite faire la même chose avec mon projet ASP.NET MVC 5. Comment gérer l'injection de dépendance avec ASP.MVC 5?

En outre, l'injection de dépendance est-elle limitée aux contrôleurs ou peut-elle fonctionner avec n'importe quelle classe?

23
Jaylen

Dans ASP.Net MVC, vous pouvez utiliser le .Net Core DI de NuGet plutôt que l'une des alternatives tierces: -

using Microsoft.Extensions.DependencyInjection

Pour la classe de démarrage/configuration MVC: -

public void Configuration(IAppBuilder app)
        {
            // We will use Dependency Injection for all controllers and other classes, so we'll need a service collection
            var services = new ServiceCollection();

            // configure all of the services required for DI
            ConfigureServices(services);

            // Configure authentication
            ConfigureAuth(app);

            // Create a new resolver from our own default implementation
            var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());

            // Set the application resolver to our default resolver. This comes from "System.Web.Mvc"
            //Other services may be added elsewhere through time
            DependencyResolver.SetResolver(resolver);
        }

Mon projet utilise Identity User et j'ai remplacé la configuration de démarrage OWIN pour suivre une approche basée sur les services. Les classes d'utilisateurs par défaut utilisent des méthodes fabriques statiques pour créer des instances. J'ai déplacé ce code dans les constructeurs et me suis fié à DI pour fournir l'injection appropriée. Il y a encore du travail en cours mais voici où je suis à: -

 public void ConfigureServices(IServiceCollection services)
        {               
            //====================================================
            // Create the DB context for the IDENTITY database
            //====================================================
            // Add a database context - this can be instantiated with no parameters
            services.AddTransient(typeof(ApplicationDbContext));

            //====================================================
            // ApplicationUserManager
            //====================================================
            // instantiation requires the following instance of the Identity database
            services.AddTransient(typeof(IUserStore<ApplicationUser>), p => new UserStore<ApplicationUser>(new ApplicationDbContext()));

            // with the above defined, we can add the user manager class as a type
            services.AddTransient(typeof(ApplicationUserManager));

            //====================================================
            // ApplicationSignInManager
            //====================================================
            // instantiation requires two parameters, [ApplicationUserManager] (defined above) and [IAuthenticationManager]
            services.AddTransient(typeof(Microsoft.Owin.Security.IAuthenticationManager), p => new OwinContext().Authentication);
            services.AddTransient(typeof(ApplicationSignInManager));

            //====================================================
            // ApplicationRoleManager
            //====================================================
            // Maps the rolemanager of identity role to the concrete role manager type
            services.AddTransient<RoleManager<IdentityRole>, ApplicationRoleManager>();

            // Maps the role store role to the implemented type
            services.AddTransient<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
            services.AddTransient(typeof(ApplicationRoleManager));

            //====================================================
            // Add all controllers as services
            //====================================================
            services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
                .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
            .Where(t => typeof(IController).IsAssignableFrom(t)
            || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
        }

La classe de contrôleur de compte a le constructeur unique: -

[Authorize]
public class AccountController : Controller
{
    private ApplicationSignInManager _signInManager;
    private ApplicationUserManager _userManager;
    private RoleManager<IdentityRole> _roleManager;

    public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, RoleManager<IdentityRole> roleManager)
    {
        UserManager = userManager;
        SignInManager = signInManager;
        RoleManager = roleManager;
    }
14
pixelda

Pour cette réponse, j'ai téléchargé Microsoft Exemple de projet WebApi comme base pour l'exemple et y ai ajouté les services DI comme suit:

  • Mettre à jour le cadre cible à la version 4.6.1
  • NuGet le package DI: - Microsoft.Extensions.DependencyInjection

Après la configuration standard de MapHttpRoute, ajoutez du code pour enregistrer les services dont vous avez besoin.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

using Microsoft.Extensions.DependencyInjection;
using System.Web.Http.Dependencies;
using ProductsApp.Controllers;

namespace ProductsApp
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );


            // create the DI services and make the default resolver
            var services = new ServiceCollection();
            services.AddTransient(typeof(DefaultProduct));
            services.AddTransient(typeof(ProductsController));

            var resolver = new MyDependencyResolver(services.BuildServiceProvider());
            config.DependencyResolver = resolver;
        }
    }

    public class DefaultProduct : ProductsApp.Models.Product
    {
        public DefaultProduct()
        {
            this.Category = "Computing";
            this.Id = 999;
            this.Name = "Direct Injection";
            this.Price = 99.99M;
        }
    }

    /// <summary>
    /// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
    /// </summary>
    public class MyDependencyResolver : IDependencyResolver
    {
        protected IServiceProvider _serviceProvider;

        public MyDependencyResolver(IServiceProvider serviceProvider)
        {
            this._serviceProvider = serviceProvider;
        }

        public IDependencyScope BeginScope()
        {
            return this;
        }

        public void Dispose()
        {

        }

        public object GetService(Type serviceType)
        {
            return this._serviceProvider.GetService(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return this._serviceProvider.GetServices(serviceType);
        }

        public void AddService()
        {

        }
    }

    public static class ServiceProviderExtensions
    {
        public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
        {
            foreach (var type in serviceTypes)
            {
                services.AddTransient(type);
            }

            return services;
        }
    }
}

J'ai ensuite modifié le contrôleur existant pour prendre le type DI (notez qu'il n'y a qu'un seul ctor)

using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        DefaultProduct _dp = null;

        public ProductsController(DefaultProduct dp)
        {
            _dp = dp;
            //
            products.Add(dp);
        }

        List<Product> products = new List<Product>()
        {
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
        };

        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
    }
}
7
pixelda

Je vous recommande d’utiliser Autofac , il existe d’autres méthodes similaires à l’unité, ninject, les repères autofac ont d’excellentes performances.

http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison

Voici l'intégration avec MVC (et fonctionne avec toutes les classes)

http://docs.autofac.org/en/latest/integration/mvc.html

4
Dei Revoledo

Le moyen le plus simple d'implémenter Dependency Injection dans ASP.NET MVC 5 consiste à utiliser l'outil développé par Microsoft lui-même, appelé Unity.

Vous pouvez trouver de nombreuses ressources sur Internet à ce sujet et vous pouvez commencer par lire la documentation officielle disponible ici: Guide du développeur pour l’injection de dépendances avec Unity

En outre, l'injection de dépendance est-elle limitée aux contrôleurs ou peut-elle fonctionner avec n'importe quelle classe?

Cela fonctionne avec n'importe quelle classe, dans n'importe quel projet, tant que vous enregistrez l'interface liée à l'implémentation (si vous souhaitez tirer profit du motif IoC ), il vous suffit d'ajouter l'instanciation de l'interface. dans votre constructeur.

2
Sakuto

Mon résolveur de dépendance par défaut

/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
    /// <summary>
    /// Provides the service that holds the services
    /// </summary>
    protected IServiceProvider serviceProvider;

    /// <summary>
    /// Create the service resolver using the service provided (Direct Injection pattern)
    /// </summary>
    /// <param name="serviceProvider"></param>
    public DefaultDependencyResolver(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    /// <summary>
    /// Get a service by type - assume you get the first one encountered
    /// </summary>
    /// <param name="serviceType"></param>
    /// <returns></returns>
    public object GetService(Type serviceType)
    {
        return this.serviceProvider.GetService(serviceType);
    }

    /// <summary>
    /// Get all services of a type
    /// </summary>
    /// <param name="serviceType"></param>
    /// <returns></returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.serviceProvider.GetServices(serviceType);
    }
}
2
pixelda

Dans cette vidéo, une injection de dépendance de démos Microsoft MVP dans MVC5 avec AutoFac. Explication très claire sur la manière de le configurer:

Demo Injection MVC5 Demo

Le code source est disponible sur GitHub

1
Edward Pescetto

Je recommande d'utiliser Windsor, en installant le package de nuget Castle Windsor MVC Bootstrapper, vous pouvez ensuite créer un service qui implémente IWindsorInstaller, à peu près comme ceci:

public class ServiceRegister : IWindsorInstaller
{
    public void Install(Castle.Windsor.IWindsorContainer container,
    Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        SomeTypeRequiredByConstructor context = new SomeTypeRequiredByConstructor ();

        container.Register(
            Component
                .For<IServiceToRegister>()
                .ImplementedBy<ServiceToRegister>().
             DependsOn(Dependency.OnValue<SomeTypeRequiredByConstructor>(context))//This is in case your service has parametrize constructoe
                .LifestyleTransient());
    }
}

Ensuite, dans votre contrôleur, quelque chose comme ceci:

public class MyController 
{
    IServiceToRegister _serviceToRegister;

    public MyController (IServiceToRegister serviceToRegister)
    {
        _serviceToRegister = serviceToRegister;//Then you can use it inside your controller
    }
}

Et par défaut, la bibliothèque se chargera d’envoyer le bon service à votre contrôleur en appelant la install() de ServiceRegister au démarrage car elle implémentera IWindsorInstaller.

0
Ali Ezzat Odeh