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?
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;
}
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:
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);
}
}
}
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)
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.
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);
}
}
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:
Le code source est disponible sur GitHub
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
.