I'm in a very specific situation where I'd like to override the default ASP.NET Core ControllerFactory. I'd like to do this because I want to be in full control of what type of controller I handle each request with.
The scenario is:
Request comes in with a specific subdomain
Based on this subdomain, I want to resolve a Generic type controller in the factory
For example:
category.website.com is called
We see it's of type category and will use the generic HomeController<T> , using DI to inject the category so the type is of HomeController<Category>
The HomeController<Category> will use some generic methods on type Category methods to render the homepage.
If I'm led to believe this link, a factory of type DefaultControllerFactory is registered on startup of the application. This seems to not be overridable.
Any idea how I would go by this? The most logical options for us is using the old ASP.NET MVC version which allows you to set your own ControllerFactory, but we'd lose features like being able to use SpaServices to prerender our Angular application.
Register your own implementation in ConfigureServices, after calling AddMvc:
services.AddSingleton<IControllerFactory, MyCustomControllerFactory>();
This way it will get called whenever a controller is to be built.
For completeness the best way is to actually implement an IControllerActivator and register it, since the default controller factory is not public. It will use whatever implementation of IControllerActivator is registered to actually create the controller class.
Related
So I want to do some profiling on a bunch of controllers which inherit from System.Web.Http.ApiController. In the project that's doing the profiling, we're registering them with
builder.RegisterApiControllers(typeof(Web.Modules.AutofacModule).Assembly)
.As(type => new Autofac.Core.KeyedService("api", type));
Later on, I'm trying to register a decorator for each with
builder.RegisterDecorator<ApiController>(original => Decorate(original, profiler),
fromKey: "api");
where Decorate injects some profiling code via a DelegatingHandler and returns the original.
I can resolve the controllers just fine:
scope.ResolveKeyed<RegistrationController>("api");
but the profiling code is never invoked, nor is Decorate.
My first thought was that maybe I need to register the controller components as ApiController's, but dropping an .As<ApiController>() just below first snippet wasn't successful.
Any help would be super. This probably just boils down to my lack of Autofac-fu.
So I want to do some profiling on a bunch of controllers which inherit from System.Web.Http.ApiController.
This is impossible. This has nothing to do with Autofac, but with the way ASP.NET Web API is designed.
Even though Web API Controllers derive from a common base class, ASP.NET Web API requires the original controller type to be used. In other words, when it requests a HomeController from the IHttpControllerActivator, it expects that exact type (or a sub type), but not a sibling type (another ApiController derivative).
I think this limitation exists because Web API uses reflection to find the actual action methods. But when you return a decorator, those methods are gone, because a decorator applies composition instead of inheritance. This is very different from how ASP.NET MVC is designed. MVC actually does allow controllers to be decorated, as it always invokes the IController.Execute method. Implementation IController is therefore MVC's only requirement for controllers.
So even if you configure Autofac to wrap any ApiController derivatives in a Decorator, Web API simply won't let you.
The way to apply Cross-Cutting Concerns around the invocation of action methods in Web API is through the use of delegating handlers.
I am having a master data service which is a generic one. Example: IMasterDataService<T>
I am trying to build an Api controller for this in a similar fashion like MasterDataController<T>. I want to resolve the controller using autofac, but I can't find any documentation for the same.
When I tried, I'm getting the error message as unable to resolve the type.
I am registering like builder.RegisterGeneric for the service, but controller I'm not getting instance and I would like to know if this is possible at all.
Well, anyways, you cannot resolve generic class at runtime. You need to parameterize it. This means that before resolving generic controller you must have a type to close this generic. The only reasonable source of said type that comes to my mind is the request itself, otherwise controller would not be generic, it just doesn't make sense to me. So, you'll need to craft your type using incoming request somehow, and then resolve your controller based on that type using some kind of custom controller factory or activator or whatever it is called in the framework you're using. In the .NET Core, for example, that would be IControllerActivator, and you'll need to register controllers in the DI container using .AddControllersAsServices() extension before doing that.
EDIT There's actually such an implementation for dotnet core right in the Microsoft's documentation. But they use slightly different technique: they do not actually register an open generic controller. They make all predefined variants of closed generic controllers and register them instead. I think that this is due to the fact that Microsoft's IoC container cannot resolve closed generic types from open generic registrations - at least it could not do that in .NET Core 1.x, I haven't tried it in 2.0 yet. However, it is possible with Autofac, so I think such an approach should work.
Another possibility here could be to inject IComponentContext into non-generic controller and resolve master data service "on the fly" in the controller itself crafting the type needed to close generic right there in the controller.
Anyways, this is a generic answer to a generic question. If you need more detailed answer you'll need to provide more insight into what you're doing.
Due to limitations on the server side I need to build a self hosting web service in .NET...
I'm currently looking at HttpSelfHostServer but I'm very un-cool with it's magical construction of the controller, that seems to be, based on name alone and using only the default constructor.
For my tests I need to inject a value into the controller and using an IoC framework for this one usage where vanilla dependancy injection will suffice is over the top.
Is there any way I can do this with HttpSelfHostServer? or are there other, non-deprecated, self-hosting alternatives that give me control over my controller constructor calls.
The HttpSelfHostConfiguration instance that you need to provide to the HttpSelfHostServer constructor inherits the DependencyResolver property from the HttpConfiguration class. Hence, you can provide a custom dependency resolver as outlined here, for instance.
I was able to override IHttpControllerActivator in a unit test, I think it's similar to what you're trying to do.
e.g.
httpConfiguration.Services.Replace(typeof(IHttpControllerActivator),
Mock<IHttpControllerActivator>());
(there's more info. about services here)
I found this Post (How to extend where MVC looks for views) about changing the location of the View.
I was wondering if there's something similar for changing the location of the controller.
I just want to change the location of the class inside project and don't want to affect the url.
For example Instead of placing the Controller into
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
I want to achieve something like
MyMvcProject\MyGroup1\
MyController1.cs
MyController2.cs
MyMvcProject\MyGroup2\
MyController3.cs
and also support Areas:
MyMvcProject\Areas\MyGroup3\
MyController4.cs
Is it possible to achieve this? And if yes, where can I find documentation about it?
You can do what you want, and it doesn't require any special configuration, because ASP.NET MVC does not care about where you put your controllers. First, controllers are located using reflection, so the name of the folder where you put your controllers is irrelevant. Controllers are searched by type name and optionally by namespace (for disambiguation). You can even have controllers in separate projects/assemblies. As long as the controller ends up in an assembly in the bin folder then it's searchable by the framework.
As mentioned above, you'll need to create a controller factory to support your custom resolution. Here's an example:
http://develoq.net/2010/custom-controller-factory-in-asp-net-mvc/
As others have already stated you need to do one of the following:
Derive from IControllerFactory interface and provide an implementation of the CreateController and ReleaseController methods.
Derive from DefaultControllerFactory and override the default behaviours.
Here are some links to get you started:
Custom controller factory in ASP.Net
Inside the ASP.NET MVC Controller factory
Dive deep into MVC - IControllerFactory
Also, if you're willing to spend a bit of money I would also recommend the book Pro ASP.NET MVC 3 Framework as this explains almost every aspect of how the MVC framework can be customised (including an example on how to create a custom controller factory - the source code for which can be freely downloaded from the publishers website).
I think it is impossible to do this. ASP.NET MVC have defined the convention that we have to follow.
Controllers are in Controllers folder, views are in Views{ControllerName}\
I believe you cannot change the convention unless you create your own ControllerFactory.
If you really want to do that, just implement IControllerFactory interface (or try to derive from DefaultControllerFactory).
Then your Application_Start register your controller factory using ControllerBuilder.Current.SetControllerFactory method.
Look at the ControllerFactory documentation and to the MVC source code for details.
What you're asking and what your example shows are two different things; depending on which one you want to achieve, you may or may not need to do any work.
There are two requirements for a class to be a controller in the MVC Framework:
It has to have a class name of Name + "Controller"
It has to have a parameterless public constructor.
Your sample "normal" MVC layout is actually not valid:
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
Those classes wouldn't be found by MVC because they don't have the correct name, regardless of which folder they are in.
If all you want to do is change the namespace/folder names, that "just works", assuming you name them the same as the appropriate route segment(s):
MyMvcProject\MyGroup1\
Page1Controller.cs
Page2Controller.cs
MyMvcProject\MyGroup2\
Page3Controller.cs
MyMvcProject\Areas\Area1\
Area1Page1Controller.cs
This walkthrough (written for MVC 2 but works just as well in MVC3) shows you how to support Areas with the default controller behavior.
If you actually want to name them SomethingController1 or SomethingElseController5, or otherwise change the route -> classname mappings, then you do need to implement a custom ControllerFactory, and inject it into the MVC pipeline.
There are plenty of examples on the web on how to do this, including the one posted earlier.
My views extend a base view class ive made:
public class BaseView : ViewPage
At the moment im calling ObjectFactory.GetInstance inside this class' constructor to get some interface implementations but id like to use structuremap to inject them as constructor arguments.
Im using a structuremapcontrollerfactory to create my controllers, but how can i do the same for views?
I know i can implement a custom ViewEngine, but using reflector to look at the mvc default viewengine and its dependencies, it seems to go on and on and i'd rather not have to re-implement stuff thats already there. Has anyone got a cunning idea how to solve this?
I know i could make things easier with setter instead of constructor injection but id rather avoid that if possible.
Yes, if you are talking about the WebForms ViewEngine, you are correct in your assessment that it was not designed with inversion of control in mind. You can either lobby Microsoft to change it, or you can use setter injection. In FubuMVC, we use setter injection along with StructureMap's BuildUp method:
WebFormsControlBuilder creates
the instance of the view
StructureMapWebFormsControlBuilder
derives from WebFormsControlBuilder -
calls the base to create the
instance, and then calls BuildUp to
perform setter injection on all
applicable properites.