Reputation:
I am trying to migrate an MVC 5 Application to ASP.NET 5 MVC 6 (Beta 7).
Having problems when using the @inherits
and @model
directive together.
Works fine when they are used separately.
In my _ViewImports
i added the @inherits
directive to use a base page with some custom user properties.
public abstract class BaseViewPage<TModel> : RazorPage<TModel>
{
protected MyPrincipal AppUser
{
get
{
return new MyPrincipal(this.User as ClaimsPrincipal);
}
}
}
_ViewImports.cshttml
@inherits CommonWeb.BaseViewPage<TModel>
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
And then i can go AppUser. in all my views.
This works if i dont use a strongly typed view. If i add the @model
directive in any view the inherited view page goes away.
Help appreciated
Update:
I did this successfully by using a custom pageBaseType in the web.config in prior versions.
Workaround.
public class ViewHelper
{
ViewContext _context;
public ViewHelper(ViewContext context)
{
_context = context;
}
public MyPrincipal AppUser
{
get
{
return new MyPrincipal(_context.HttpContext.User as ClaimsPrincipal);
}
}
public string ControllerName
{
get
{
return _context.RouteData.Values["controller"].ToString();
}
}
}
View:
@{ var viewHelper = new ViewHelper(ViewContext);}
A way to achieve this for all views?
Upvotes: 4
Views: 2324
Reputation: 35032
There is a better way in MVC 6, which now supports injecting dependencies on the views with the @inject
directive. (The directive @inject IFoo Foo
allows you to use in your view a property named Foo
of type IFoo
)
Create a new interface IAppUserAccessor
for getting your app user, for example:
public interface IAppUserAccessor
{
MyPrincipal GetAppUser();
}
Create a class AppUserAccessor
implementing it:
public class AppUserAccessor : IAppUserAccessor
{
private IHttpContextAccessor httpContextProvider;
public AppUserAccessor(IHttpContextAccessor httpContextProvider)
{
this.httpContextProvider = httpContextProvider;
}
public MyPrincipal GetAppUser()
{
return new MyPrincipal (
httpContextProvider.HttpContext.User as ClaimsPrincipal);
}
}
Register the new interface in the services container by adding a new entry in the ConfigureServices
method of Startup.cs:
services.AddTransient<IAppUserAccessor, AppUserAccessor>();
Finally use the @inject
directive to inject the IAppUserAccessor
in your views. If you add the directive in ViewImports.cshtml
then it will be available on every view.
@inject WebApplication4.Services.IAppUserAccessor AppUserAccessor
With all the pieces above you can now just use it on your view(s):
@AppUserAccessor.GetAppUser()
Update
If you need to inspect the route values, like the controller name, you can inject an IActionContextAccessor
into your class and use it as follows:
public AppUserAccessor(IHttpContextAccessor httpContextProvider, IActionContextAccessor actionContextAccessor)
{
this.httpContextProvider = httpContextProvider;
this.actionContextAccessor = actionContextAccessor;
}
...
public string ControllerName
{
get { return actionContextAccessor.ActionContext.RouteData.Values["controller"].ToString(); }
}
Of course, that doesn't look like an AppUserAccessor
anymore and smells like it has different responsabilities. At the very least it needs a more appropriate name :)
I would double check what do I need the controller name for. There might be a better way to accomplish your objective. (For example, if you need it for generating new links/urls you might just use an IUrlHelper
)
Accessing ViewContext
Looks like beta8 has added support for injecting the ViewContext, although the implementation details may change before RC. See this question
Upvotes: 4