6

tl;dr: I'm creating Type (by reflection) that is extending ApiController. How can I dynamically register it (it can be registered at startup; no need to do this at runtime).

Long story:

So in my application I have multiple interfaces, i.e.:

interface IMyInterface
{
    MyResponse Hello(MyRequest request);
}

The thing that I want to achive is for each interface create controller that should look like this:

public class IMyInterfaceController : ApiController
{
    public IMyInterface MyInterface { get; set; }

    public MyResponse Hello([FromBody] MyRequest request)
    {
        return MyInterface.Hello(request);
    }
}

Generating this controller is already done using heavy C# reflection. The thing is that I want to do right now is to register this controller under /api/{controller}/{action}.

In Global.asax right now I got this:

public class MvcApplication : System.Web.HttpApplication
{
    private readonly InterfaceReader _reader = new InterfaceReader(); // this class is doing all staff with reflection to create controller class

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);

        var controller = _reader.CreateController(new MyImplementation()); // MuImplementation implements IMyInterface
    }
}
2
  • MAGx2, could you please give a feedback if any of the provided solutions helped you? Marking the answer that worked for you may help other visitors in the future. Commented Jun 16, 2016 at 6:05
  • Sorry for the delay. I've already accepted your answer. Commented Jun 16, 2016 at 8:57

2 Answers 2

5

Solution with IHttpControllerFactory

I guess that what you need is a controller factory:

public class MyHttpControllerFactory : IHttpControllerFactory
{
    private readonly InterfaceReader _reader;
    private readonly HttpConfiguration _configuration;

    public MyHttpControllerFactory(InterfaceReader reader, HttpConfiguration configuration)
    {
        _reader = reader;
        _configuration = configuration;
    }

    public IHttpController CreateController(HttpControllerContext controllerContext, string controllerName)
    {
        if (controllerName == null)
        {
            throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", controllerContext.Request.RequestUri.AbsolutePath));
        }

        // Change the line below to whatever suits your needs.
        var controller = _reader.CreateController(new MyImplementation());
        controllerContext.Controller = controller;
        controllerContext.ControllerDescriptor = new HttpControllerDescriptor(configuration, controllerName, controller.GetType());

        return controllerContext.Controller;
    }

    public void ReleaseController(IHttpController controller)
    {
        // You may want to be able to release the controller as well.
    }
}

Then in the Global.asax you need to register the custom controller factory:

public class MvcApplication : System.Web.HttpApplication
{
    private readonly InterfaceReader _reader = new InterfaceReader(); // this class is doing all staff with reflection to create controller class

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);

        GlobalConfiguration.Configuration.ServiceResolver.SetService(typeof(IHttpControllerFactory), new MyHttpControllerFactory(_reader, GlobalConfiguration.Configuration));
    }
}

Solution with IHttpControllerActivator

If you use Web Api 2 then the solution is to use either IDependencyResolver or IHttpControllerActivator instead of the factory. I guess that the IHttpControllerActivator is better option in your case.

public class MyServiceActivator : IHttpControllerActivator
{
    private readonly InterfaceReader _reader;
    private readonly HttpConfiguration _configuration;

    public MyServiceActivator(InterfaceReader reader, HttpConfiguration configuration)
    {
        _reader = reader;
        _configuration = configuration;
    }

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        // Change the line below to whatever suits your needs.
        var controller = _reader.CreateController(new MyImplementation());
        return controller;
    }
}

Then in the Global.asax you need to register the custom activator:

public class MvcApplication : System.Web.HttpApplication
{
    // this class is doing all staff with reflection to create controller class
    private readonly InterfaceReader _reader = new InterfaceReader();

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);

        HttpConfiguration config = GlobalConfiguration.Configuration;
        config.Services.Replace(typeof(IHttpControllerActivator), new MyServiceActivator(_reader, config));
    }
}

I hope this helps.

Sign up to request clarification or add additional context in comments.

6 Comments

In method GetControllerInstance I got error because ApiController is not inheriting IController
@MAGx2 what I wrote before was suitable for MVC not WebApi, please see the updated answer.
I do not have IHttpControllerFactory. As I see it was replaced stackoverflow.com/a/19640853/1819402
@MAGx2 , please see the updated answer once again. Hopefully the solution with IHttpControllerActivator is what you are looking for.
Can you please paste a gist for InterfaceReader & MyServiceActivator ? we are in a situation where we need to create controller on the fly.
|
1

You need to create your own class that implements IHttpControllerActivator

Here's an example that I use for DI with Windsor

public class WindsorCompositionRoot : IHttpControllerActivator
{
    private readonly IWindsorContainer _container;

    public WindsorCompositionRoot(IWindsorContainer container)
    {
        _container = container;
    }

    public IHttpController Create(
        HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        var controller =
            (IHttpController) _container.Resolve(controllerType);

        request.RegisterForDispose(
            new Release(
                () => _container.Release(controller)));

        return controller;
    }

    private class Release : IDisposable
    {
        private readonly Action _release;

        public Release(Action release)
        {
            _release = release;
        }

        public void Dispose()
        {
            _release();
        }
    }
}

Then you need to wire it up in the Global.asax in Application_Start()

        Container = new WindsorContainer().Install(FromAssembly.This());

        GlobalConfiguration.Configuration.Services.Replace(
            typeof (IHttpControllerActivator),
            new WindsorCompositionRoot(Container));

Installing the controllers is done in a Windsor Installer class

public class ControllersInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
                            .BasedOn<IHttpController>()
                            .LifestyleTransient());
    }
}

1 Comment

In this example I don't see where my controllers are registered. Right now I cannot access them via /api/MyController/action.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.