Como criar uma injecção de dependência para ASP.NET MVC 5?

Criar uma injecção de dependência com ASP.NET o núcleo é bastante fácil. A documentação explica-o muito bem aqui e este tipo tem um vídeo assassino para o explicar.

No entanto, quero fazer o mesmo com a minha ASP.NET projecto MVC 5. Como lidar com a injecção de dependência com ASP.MVC 5?

também, a injecção de dependência é limitada apenas aos controladores ou pode funcionar com qualquer classe?

Author: Jaylen, 2017-04-09

7 answers

Em ASP.Net MVC você pode usar o. Net Core DI de NuGet em vez de uma das alternativas de Terceiros: -

using Microsoft.Extensions.DependencyInjection

Para a classe de arranque/configuração do 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);
        }

O meu projecto usa o Utilizador de identidade e substituí a configuração de arranque OWIN para seguir uma abordagem baseada em serviços. As classes de usuário de identidade padrão usam métodos de fábrica estáticos para criar instâncias. Transferi esse código para os construtores e confiei na DI para fornecer a injecção apropriada. Ainda está em andamento, mas aqui é onde estou: -

 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)));
        }

A classe do controlador de Contas tem o construtor único: -

[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;
    }
 8
Author: pixelda, 2017-12-06 12:27:54

Para esta resposta, baixei um exemplo da Microsoft do WebApi project como base para o exemplo e adicionei-lhe serviços DI da seguinte forma,

  • actualizar o quadro-alvo para 4.6.1
  • NuGet The DI package: - Microsoft.Extensao.DependencyInjection

Após a configuração padrão do MapHttpRoute, adicione código ao registo dos serviços de que necessita

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;
        }
    }
}

Em seguida, alterei o controlador existente para tomar o tipo de DI (note que existe apenas um 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);
        }
    }
}
 3
Author: pixelda, 2017-12-16 21:35:31

A forma mais simples de implementar a injecção de dependência em ASP.NET MVC 5 é usar a ferramenta desenvolvida pela própria Microsoft, chamada Unity.

Você pode encontrar muitos recursos na internet sobre isso, e você pode começar por ler a documentação oficial disponível aqui: Guia do programador para a injeção de Dependência usando Unity

Também, a injecção de dependência é limitada apenas aos controladores ou pode funcionar com qualquer classe?

Funciona com qualquer classe., em qualquer projecto, desde que registe a Interface relacionada com a implementação (se quiser tirar proveito do padrão do COI), tudo o que tem de fazer é adicionar a instanciação da Interface no seu construtor.
 2
Author: Sakuto, 2017-04-09 19:47:01

Neste vídeo, uma injeção de dependência do MVP da Microsoft em MVC5 com o AutoFac. Explicação muito clara sobre como configurar:

Demonstração da injeção de Dependência MVC5

O código-fonte está disponível em GitHub

 1
Author: Edward Pescetto, 2018-08-10 11:38:12

O Meu Resolvedor De Dependências Por Omissão

/// <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);
    }
}
 0
Author: pixelda, 2018-07-10 13:20:50

Eu recomendo o uso de Windsor, Instalando o pacote nuget Castle Windsor MVC Bootstrapper, então você pode criar um serviço que implementa IWindsorInstaller, algo assim:

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());
    }
}

Então dentro do teu controlador algo assim:

public class MyController 
{
    IServiceToRegister _serviceToRegister;

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

E por padrão a biblioteca irá lidar com o envio do serviço direito para o seu controlador, chamando o install() de ServiceRegister no arranque porque implementa IWindsorInstaller

 0
Author: Ali Ezzat Odeh, 2018-08-09 16:37:41