1

How to override the default realization IControllerFactory with a custom factory for custom cases one and keep calling DefaultControllerFactory in normal cases if DefaultControllerFactory has become an inner class in ASP.NET Core 3?

services.AddSingleton<IControllerFactory, MyCustomControllerFactory>();
// this class for .NET core 2
public class MyCustomControllerFactory : DefaultControllerFactory
{
    public override object CreateController(ControllerContext context)
    {
        //custom handling...

        //base handling
        return base.CreateController(context);
    }

    public override void ReleaseController(ControllerContext context, object controller)
    {
        base.ReleaseController(context, controller);
    }
}

The DefaultControllerFactory class in .NET 5 is internal and I cannot call its CreateController method to try to get regular controllers registered in the ASP MVC Core 5 environment.

//this class for .NET core 3 or .NET 5 
    public class MyCustomControllerFactory : IControllerFactory
    {
        public object CreateController(ControllerContext context)
        {
            if(/*is custom case*/) 
            { 
                /*custom actions*/ 
                return /*custom IController*/ 
            }
            return /*in this place I want calling base.CreateController(context)*/;
        }

        public void ReleaseController(ControllerContext context, object controller)
        {
            var disposable = controller as IDisposable;
            if (disposable != null) { disposable.Dispose(); }
        }
    }
  • maybe implementing the `IControllerFactory `? – benuto Jul 13 '21 at 23:22
  • Yes, if this code converts to .NET 5 then it will use IControllerFactory as a parent, but how to call DefaultControllerFactory for normal cases if the custom factory for custom cases is already registered in DI for IControllerFactory before calling AddMvc () in ConfigureServices? – Андрей Тарусин Jul 14 '21 at 04:45
  • 1
    `DefaultControllerFactory` looks simple enough to just copy the code into your type. The bigger question is why? And can you avoid implementing your own factory? – Jeremy Lakeman Jul 14 '21 at 05:40
  • Just copy code isn't a possibility, because it uses internal types. And I have must migrate this code from .Net 4.8 to .Net 5 because it is part of the bigger solution. Maybe in the future, this will be trash, but it is needed for me now. – Андрей Тарусин Jul 15 '21 at 14:40

1 Answers1

1

We can overload IControllerActivator or IControllerFactory. This method will be always called, which creates the controller.

public class Startup
{
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddSingleton<IControllerActivator, CustomControllerActivator>();
            services.AddMvc(options => { });
        }
}

We can still call the main platform functions to create controllers. We just need to get a list of all registered instances for IControllerActivator or IControllerFactory and excluding our implementation from this list, one by one, try to create a controller with each of this list. It is important to understand that this implementation is simplified as much as possible and can loop if re-added to DI.

    public class CustomControllerActivator : IControllerActivator
    {
        private static object CreateController(IEnumerable<IControllerActivator> activators, ControllerContext context)
        {
            if (activators is null)
            {
                throw new ArgumentNullException(nameof(activators));
            }

            foreach (IControllerActivator activator in activators)
            {
                object controller = activator.Create(context);

                if (controller != default)
                    return controller;
            }

            return default;
        }

        private readonly IServiceProvider _provider;

        private IEnumerable<IControllerActivator> _otherActivators;

        private IEnumerable<IControllerActivator> OtherActivators
        {
            get
            {
                if (_otherActivators == null)
                    _otherActivators = _provider.GetServices<IControllerActivator>()?
                        .Where(o => !object.ReferenceEquals(this, o));

                return _otherActivators;
            }
        }
        
        public CustomControllerActivator(IServiceProvider provider)
        {
            _provider = provider ?? throw new ArgumentNullException(nameof(provider));
        }
        
        public object Create(ControllerContext controllerContext)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException(nameof(controllerContext));
            }

            if (controllerContext.ActionDescriptor == null)
            {
                throw new ArgumentException(nameof(controllerContext.ActionDescriptor));
            }

            var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;

            if (controllerTypeInfo == null)
            {
                throw new ArgumentException(nameof(controllerContext.ActionDescriptor.ControllerTypeInfo));
            }

            if (!IsNeedThisCustomController(controllerContext))
                return CreateController(OtherActivators, controllerContext);

            return CreateCustomController(controllerContext);
        }

        /// <summary>
        /// Do need to create this custom controller
        /// </summary>
        /// <param name="controllerContext"></param>
        /// <returns></returns>
        private bool IsNeedThisCustomController(ControllerContext controllerContext)
        {
            return false;
        }

        /// <summary>
        /// Сreating a custom controller
        /// </summary>
        /// <param name="controllerContext"></param>
        /// <returns></returns>
        private object CreateCustomController(ControllerContext controllerContext)
        {
            return default;
        }
        
        public void Release(ControllerContext context, object controller)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (controller == null)
            {
                throw new ArgumentNullException(nameof(controller));
            }

            if (controller is IDisposable disposable)
            {
                disposable.Dispose();
            }
        }
    }