@inject razor views #362

Closed
mathysd opened this Issue Dec 24, 2016 · 4 comments

Projects

None yet

2 participants

@mathysd
mathysd commented Dec 24, 2016

Good job on the migration to .net core!
Will there be support for @inject inside razors views? Or did I miss something?

As explained here

@dotnetjunkie
Collaborator
dotnetjunkie commented Dec 24, 2016 edited

I have to look into that, but my gut feeling is that this is a feature that shouldn't exist at all. Views should not have dependencies. Views should be dumb and all reqyired data should be supplied to the view by the controller.

@dotnetjunkie
Collaborator

After some investigation, I have to come to the conclusion that ASP.NET Core MVC currently lacks the proper hook that allows intercepting injection of Razor View Properties (i.e. services that are injected using @inject).

You can see that the way to resolve properties is hard-wired into the RazorPageActivator at line 204. At that line, GetRequiredService is called directly on the HttpContext.RequestServices object, which always points to the built-in container.

The only way to intercept this is by replacing the default RazorPageActivator with a custom implementation, because the RazorPageActivator doesn't have any virtual methods that allow us to override, nor does it depend on a dependency (e.g. an imaginary IRazorPagePropertyActivator) that we can override.

This design flow means that the only way to add this behavior is to copy all the code from the original RazorPageActivator (and the internal PropertyActivator<TContext> and PropertyHelper as well) into your custom implementation of IRazorPageActivator. This allows you to make a few changes that would allow you to intercept the creation of properties to your own container.

This solution however isn't really feasible, because it causes a severe 'synchronization burden' on you, because you will have to keep these three files in sync with any possible changes and bug fixes that Microsoft will do on these three classes itself.

This does mean that the statements I made months ago about MVC having all the required interception points in place, where unfortunately false. This is an omission in the design of MVC.

But as I said previously, IMO from a design perspective, views should not have any dependencies; not even for "localization or data required only for populating view elements". This is better done at the controller level (or perhaps at a level between the controller and the view, by using decoration or interception to enrich this data). This keeps the views truly dumb.

@mathysd
mathysd commented Dec 25, 2016 edited

Thanks for looking into this and I agree with you that views should be kept simple. I have a shared layout that displays some information about the current user and I don't want every controller to get this information and "push" it to the layout. But for now I think I can solve it with a view component that does support constructor injection.

As a last resort I could cross-wire the asp.mvc container to make use of the simple injector container to resolve dependencies for the views. Something like:

services.AddScoped<ISomething>(provider => container.GetInstance<ISomething>());
Pragmatic aproach but it feels a bit hacky :)

@dotnetjunkie
Collaborator
dotnetjunkie commented Dec 25, 2016 edited

That isn't that hacky at all, I must say. What you are doing is called "cross-wiring" and is almost always required when to run an application-specific container besides the built-in framework container. You sometimes have to cross-wire registrations from one side to the other. You want to keep the number of cross-wired dependencies (on both sides) to a minimum, but you will always need a few.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment