StructureMap Dependency Resolution : Manually get instance from ActionFilter - structuremap

I'm trying to follow this tutorial using StructureMap : http://iridescence.no/post/Constructor-Injection-for-ASPNET-MVC-Action-Filters.aspx
What I'm trying to figure out is the StructureMap equivalent of this line:
var container = (ICanResolveDependencies) HttpContext.Current.ApplicationInstance;
I want to get the container back, so I can resolve dependencies there manually.
This is how I am setting the dependencyresolver in global.asax
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));

You could just require IContainer dependency in your action filter's constructor. If it's not registered automatically by StructureMap, you could register it with:
For<IContainer>().Use<Container>();
Edit
Option 1: Couldn't you just use something like:
GlobalConfiguration.Configuration.ServiceResolver.GetService(...)
// or (not sure what would be the right syntax)
GlobalConfiguration.Configuration.ServiceResolver.Current.GetService(...)
There got to be some way of retrieving the current service resolver once you set it.
Option 2: With regular MVC you can get the current resolver like this:
DependencyResolver.Current
And use it like this:
DependencyResolver.Current.GetService()
Looks like WebAPI doesn't use DependencyResolver, but according to this blog post, you could set it like this:
DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
// this override is needed because WebAPI is not using DependencyResolver to build controllers
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
DependencyResolver.Current.GetService,
DependencyResolver.Current.GetServices);
Now try to use DependencyResolver.Current from your action filter.
Option 3: Use ObjectFactory.GetInstance directly - Probably not the best idea in MVC project, since it should already be encapsulated in IDependencyResolver instance.

Related

Working with helpers in the dotnetcore DI world

I like to have static helper classes in my apps for common plumbing stuff, like checking roles, or Razor Html helpers and stuff... but how do you do this in the DI world?
Like lets say I want an extension helper to check if a user is an admin
public static async Task<bool> IsAdmin(this ApplicationUser user)
...
if(user.IsAdmin()){...}
So pre-core I could just ignore DI and create a UserManager all inside IsAdmin do whatever I need to do. But now is there no way to get the UserManager in these helpers to just use? The only way I can see is to inject it into the Controller, then pass along into the method (which I find ugly). Then there's the issue of trying to do user.IsAdmin() in the Razor view, would I need to add the UserManager to the ViewData collection to get it into the view markup?
Am I just missing something here?
Thanks,
Steve
First of all if you just asked how to use static class with di, i would say that your question is duplicate of How to use DI inside a static Method in Asp.net Core rc1
But as i see, you have some other question?
But now is there no way to get the UserManager in these helpers to
just use?
Yes there is a way : Service Locator pattern. But it is an anti pattern (see this article) . As far as possible you should avoid to use this pattern. Also see discussion in github.
The only way I can see is to inject it into the Controller, then pass
along into the method (which I find ugly)
I think this way is better than you want. I would prefer this.
Then there's the issue of trying to do user.IsAdmin() in the Razor
view, would I need to add the UserManager to the ViewData collection
to get it into the view markup?
In Aspnet core you can inject a dependency into a view, so you don't need to use ViewData. Simply you can inject UserManager into your view and then pass it as parameter to method. Take a look at official docs
Well you are talking about a cross-cutting concern here and one way how I've seen cross cutting concerns solved in ASP.NET Core MVC is with attributes (like [Authorize] for example). Which I think is an elegant solution.
So, if I understand your question correctly I think you can solve this with an Action Filter. Damien Bod described a few days ago how to use ActionFilters: https://damienbod.com/2016/09/09/asp-net-core-action-arguments-validation-using-an-actionfilter/.
So in short, you inherit from ActionFilterAttribute and make your own curstom filter called MyCustomFilter or whatever. Have this MyCustomFilter request UserManager in its constructor via DI. Then above any action method in a controller you say:
ServiceFilter[typeof(MyCustomFilter)]
And in MyCustomFilter you ofcourse have logic to check if User is IsAdmin and then take action accordingly.
Now, I've always used Microsoft's Unity to handle cross cutting concerns via interception (you can read more about that here: https://dannyvanderkraan.wordpress.com/2015/09/30/real-world-example-of-adding-auditing-with-dependency-injections-interception/. But last time I checked there is no Unity container for asp.net core yet. But this guy has a great article about porting it to core: https://dzimchuk.net/post/bring-your-own-di-container-to-aspnet-5-unity. I would really like my Interception back! Very elegant solution to cross cutting concerns. They are working on it though: https://github.com/unitycontainer/unity/issues/66. Fingers crossed...

MVC5 - OWIN - IAuthenticationManager

I am using structuremap.mvc5 although this question would apply equally to any DI container. I need to configure the container such that it can create a controller that takes Microsoft.Owin.Security.IAuthenticationManager. I am configuring the container via a PreApplicationStartMethod. Is an instance of Microsoft.Owin.Security.IAuthenticationManager available at this time, or is there something I can use as a delegate?
Found it:
_.For<IAuthenticationManager>().UseSpecial(expression =>
expression.ConstructedBy( i => HttpContext.Current.GetOwinContext().Authentication));

Castle.Windsor and HttpContextWrapper

HttpContextWrapper and HttpContextBase, as explained here, were introduced to make HttpContext more mockable/testable.
I'm trying to use it with S#arp Architecture, and hitting some problems.
My MVC Controllers are set up to accept an HttpContextBase argument in the constructor, and during Application_Start, HttpContextBase is registered with Castle.Windor as follows:
container.Register(Component.For<HttpContextBase>().UsingFactoryMethod(
() => new HttpContextWrapper(HttpContext.Current)));
This seemed to work OK for a bit, but then I realised Castle is only running that Factory method once, so all requests get the original HttpContextWrapper. Really it needs to be re-created for every request. The Castle.Windsor command for that would be:
container.Register(Component.For<HttpContextBase().
LifeStyle.PerWebRequest.UsingFactoryMethod(
() => new HttpContextWrapper(HttpContext.Current)));
... but it turns out that Castle.Windsor doesn't allow LifeStyle.PerWebRequest to be used within Application_Start (as explained here)
What should I be doing? Is there an easy way round this or should I give up on HttpContextWrapper and inject my own factory to make new ones as needed?
My MVC Controllers are set up to accept an HttpContextBase argument in the constructor
You gotta be doing something extremely wrong here, so stop before it's too late and damage has been caused (material, moral and human casualties :-)). You already have the HttpContext inside the controller.
Don't register any HttpContexts in your DI framework. The HttpContext handling is the job of ASP.NET.
As Darin noted, it makes no sense to inject an HttpContext into an MVC controller. However, if you need it for other kind of services and also need it in Application_Start(), use an hybrid perwebrequest-transient lifestyle. Or, since it's trivial to build, just make it transient.
As others have stated - you are doing it wrong. My big question is:
What are you doing that requires you to inject HttpContextBase in your controller? It might be more helpful to people wanting to help you if you would provide us more context about what you are really trying to do. Lets take Castle out of it and get down to what your controller is doing.
BTW, your controller already has a reference to HttpContext. If you are doing this for testability, there is nothing you need to do at the controller level. You would just need to mock the HttpContextBase object as needed in your controller tests.

ASP.NET MVC: Uses a delegate field as an action method?

Is it possible in ASP.NET MVC via some extension/override points to allow a "delegate field" to be used as an "action"?
Something like:
using System;
using System.Web.Mvc;
namespace Company.Web.Controllers
{
public class SwitchboardController : BaseController
{
public Func<ActionResult> Index, Admin, Data, Reports;
public SwitchboardController()
{
// Generic views
Index = Admin = Data = Reports =
() => View();
}
}
}
I know I'm a little hell-bent for this one but if this is possible it'd open up many new ways of making actions. You could, for example, have Django-style generic views in MVC with only a single line of code to define the action or have different ways to factor duplicate logic across multiple controllers.
I'm not quiet sure where would be the place to slap this logic into or how much work would be required to alter something so fundamental in the framework.
You will probably have to build your own Controller factory. This class builds controllers, and implements IControllerFactory. You can inherit from DefaultControllerFactory. Override CreateController() to return your own IController.
Register your controller factory in Application_Start() of MvcApplication using this line:
ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
In your implementation of IController, override the Execute method. You can use the RequestContext to decide which delegate to invoke. It would probably be easiest to inherit from ControllerBase, and override Execute in there if you don't want to fully implement IController.
The RequestContext passed into Execute carries a RouteData object. This is a dictionary populated by the routing engine that tells you what action should be invoked, and any parameters. You can get the action name like this:
//context is a RequestContext object passed to IController.Execute()
string actionName = requestContext.RouteData.Values["action"];
You could even define your action as a dictionary, and just pull them out once you get the action name.
One last thing, normal action methods return an ActionResult, which the framework uses to decide which view to render. Once you execute your delegates, I think you'll have to set some stuff manually in your special base controller. I'm not exactly sure what to set or how to get your View executed from here without cracking open the MVC source.
Good luck! This looks like an interesting idea.
As you seem to be implementing a BaseController in your code sample, if you override the Execute (from the IController) you'll be able to interpret the request => action however you like.
No, it isn't. The base controller is looking for methods and not for fields to dispatch an action.
EDIT:
Sorry, I was a bit fast and fixed to the standard classes provided.
You can do that but you have to overwrite the Execute Method in your controller or implement and provide your own IActionInvoker to dispatch the action to fields. Look into the post action processing in detail. It explains the dispatching in detail.

How to setup or specify a Controllers folder for ASP.NET MVC to use instead of the default Controllers folder under?

I'm using VS2008 and .net 3.5. I have created a class library(Myproject.Controllers) in my solution. Under this class, I have added a Controllers folder. And in the folder I have added a MyController which is declared as
public class MyController : Controller
My views are still in the default Views folder. Now, when I run this in VS, I get a message in the Default.aspx.cs:
{"The controller for path '/' could not be found or it does not implement IController."}
If I put a copy of my MyController in the default Controllers folder then it works fine. Does anyone know how I can set/configure the Controllers path? I've searched the web and didn't find anything for this. Thank you.
By creating your own Controller factory, you can specify exactly how controllers are used. Here's how
step 1 - create a class and derive it from IControllerFactory
http://msdn.microsoft.com/en-us/library/system.web.mvc.icontrollerfactory_members.aspx
step 2 - Consume your new Controller Factory in application start
ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
IControllerFacotry implements 2 methods
IController IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName);
void IControllerFactory.ReleaseController(IController controller);
And that's all there is to it.
I did this not too long ago, and the only tricky part was to make sure that the namespace where the controller resides is the same as it would be if it was in the default folder. The main pitfall is the root namespace of the class library project - you can change it by right-clicking the project node in Solution Explorer, selecting Properties and changing the value in the Root Namespace textbox. The easiest way is to name it the same as the MVC application itself.
From my understanding, this should just work. Did you add your Controllers library as a reference to your web site?
From MVC source
// ControllerTypeCache.cs:
private static List<Type> GetAllControllerTypes(IBuildManager buildManager) {
// Go through all assemblies referenced by the application and search for
// controllers and controller factories.
// DefaultControllerFactory.cs
// if all else fails, search every namespace
return GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */);
I had a similar issue using dotnet 4.0 utilizing MVC 2.0 on IIS7.0. After checking all the normal stuff, having the right MVC assembly version and so on, i finally moved the code from a random location on the web server to /inetpub/www and recreated my site on the webserver. That seemed to have done the trick. Aparently, DotNet MVC likes to have the source under /inetpub/www to be able to resolve the MVC paths.

Resources