aspnet / Mvc

[Archived] ASP.NET Core MVC is a model view controller framework for building dynamic web sites with clean separation of concerns, including the merged MVC, Web API, and Web Pages w/ Razor. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
5.62k stars 2.14k forks source link

autofac property injection not working in controller #8667

Closed yqszt closed 5 years ago

yqszt commented 5 years ago

Is this a Bug or Feature request?:

it is should be a Bug.

Steps to reproduce (preferably a link to a GitHub repo with a repro project):

Description of the problem:

i'm using autofac as asp.net core service provider, and i use property injection for some properties in controller, like as Logger. when i access the action method of the controller, the Logger is Null. i try to write some test code, like this:

 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            var autofacServiceProvider = new AutofacServiceProvider(services.AddLarkContainer(Configuration));

            var dataUploadController = autofacServiceProvider.GetService<DataUploadController>();

            var activatorCache = (ITypeActivatorCache)autofacServiceProvider.GetService(typeof(ITypeActivatorCache));
            var dataUploadController1 = activatorCache.CreateInstance<DataUploadController>(autofacServiceProvider, typeof(DataUploadController));

            return autofacServiceProvider;

the dataUploadController was working(Logger not null) but dataUploadController1 was not. if use constructor injection was working fine.

is this a bug or my fault?

Version of Microsoft.AspNetCore.Mvc or Microsoft.AspNetCore.App or Microsoft.AspNetCore.All:

Microsoft.AspNetCore.App: 2.1.1 Microsoft.AspNetCore.Mvc: 2.1.1 AutoFac: 4.8.1 Autofac.Extensions.DependencyInjection: 4.3.0

yqszt commented 5 years ago

i replaced the ITypeActivatorCache and IControllerFactory, then it worked. ITypeActivatorCache :

 public class LarkTypeActivatorCache : ITypeActivatorCache
    {
        public TInstance CreateInstance<TInstance>(IServiceProvider serviceProvider, Type optionType)
        {
            return (TInstance)serviceProvider.GetService(optionType);
        }
    }

IControllerFactory:
   public class LarkControllerFactory : DefaultControllerFactory
    {
        public LarkControllerFactory(IControllerActivator controllerActivator,
            IEnumerable<IControllerPropertyActivator> propertyActivators)
            :base(controllerActivator, propertyActivators)
        {
        }

        public override object CreateController(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.ActionDescriptor == null)
            {
                throw new ArgumentException(string.Format("{0},{1}",
                    nameof(ControllerContext.ActionDescriptor),
                    nameof(ControllerContext)));
            }
            var controller = ControllerActivator.Create(context);
            return controller;
        }
    }
pranavkm commented 5 years ago

@yqszt based on your update it looks like your issue was resolved. That said, there's an option to resolve controllers from DI using IMvcBuilder.AddControllersAsServices. It should resolve register and resolve controller instances from DI for you:

services.AddMvc()
    .AddControllersAsServices();

It should give you the DI behavior of the underlying constructor. Since AutoFac supports property injection, things should just work and you wouldn't have to replace the ITypeActivatorCache. I recommend going this route since ITypeActivatorCache is in the .Internal namespace and is subject to possible breaking changes in minor releases of the product.

yqszt commented 5 years ago

@pranavkm thank you so much, IMvcBuilder.AddControllersAsServices resolved my issue perfectly, although it's a magic for me now, there are lot of things in mvc, like ITypeActivatorCache,ControllerFactory,IControllerPropertyActivator etc. activate a controller instance is not as easy as i thought.