It's currently not possible to inherit RazorPage<T> - required in order to get Razor Views to inherit a Custom Base class. This is the same issue that was reported in this February Post where trying to inherit the generic RazorPage, i.e:
public abstract class ViewPage<T> : RazorPage<T> { ... }
Will throw this ArgumentException:
Property 'ViewData' is of type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary
1[[RazorRockstars.SearchRockstars, Mvc.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', but this method requires a value of type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'. Parameter name: viewContextStackTrace
at Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper
1.Contextualize(ViewContext viewContext) at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.<>c__DisplayClass16_0.<CreateActivateInfo>b__1(ViewContext context) at Microsoft.Extensions.Internal.PropertyActivator1.Activate(Object instance, TContext context) at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.Activate(IRazorPage page, ViewContext context) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) at Microsoft.AspNetCore.Mvc.Razor.RazorView.d__14.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Razor.RazorView. d__13.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at ServiceStack.Mvc.RazorFormat.RenderView(IRequest req, ViewDataDictionary viewData, IView view, String layout) in C:\src\ServiceStack\src\ServiceStack.Mvc\RazorFormat.cs:line 194
It works when you inherit RazorPage<object> however this makes @base.Model a non-useful object Type which is not ideal.
I suspect everything would Just Work:tm: if you use the type parameter TModel in your @inherits directive e.g.
c#
@model Fred
@inherits ViewPage<TModel>
Please let us know if that avoids issues.
No I get this error:
The 'inherits' keyword is not allowed when a 'model' keyword is used.
@model SearchRockstars
@inherits ViewPage<TModel>
Likewise when explicitly specifying the Type:
The 'inherits' keyword is not allowed when a 'model' keyword is used.
@model SearchRockstars
@inherits ViewPage<SearchRockstars>
@mythz The @inherits would need to exist in a _ViewImports.cshtml and the @model in your actual view.
@NTaylorMullen ok that works but the intelli-sense is broken as the tooling no longer thinks it's inheriting from ViewPage<TModel> so the development experience is much worse.
I'll have to revert back to using RazorPage<object> until the tooling matches runtime behavior.
Is there a work item here to allow @inherits ViewPage<MyModel> to work as expected or should I close this issue?
@mythz can you be more specific as to what about the development experience is broken?
When using @inherits in _ViewImports.cshtml the Razor View editor in VS.NET doesn't think it's inheriting ViewPage<T> anymore so trying to call anything on the base ViewPage<T> is treated the same way as trying to call a non-existent method or property.
Not sure if it matters but I also have the latest version of ReSharper installed
@mythz It may be a ReSharper issue because I'm able to get
C#
public abstract class CustomViewPage<TModel> : RazorPage<TModel>
{
// ....
}
Working at dev time.
@NTaylorMullen ok yeah looks like it was, disabling ASP.NET Razor in Resharper > Options > Products & Features fixed the broken references.
I'll close this issue as the suggested solution works for vanilla VS.NET (i.e. w/o R#), but it would be nice if @inherits ViewPage<MyModel> and class ViewPage<T> : RazorPage<T> { ... } also works as it's an issue devs would run into, e.g. I hit this issue independently from the Extending Razor Views in ASP.NET Core post whose suggestion of inheriting RazorPage<object> is still required when using @inherits in the Razor View.
I have runtime problems:
@inherits DefaultRazorPage<Hs.Frontend.ViewModels.NavBarViewModel> throws errors but @inherits DefaultRazorPage<object> not somehow.
message:
Property 'ViewData' is of type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]', but this method requires a value of type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'.
Parameter name: viewContext
call stack:
at Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper`1.Contextualize(ViewContext viewContext)
at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.<>c__DisplayClass16_0.<CreateActivateInfo>b__1(ViewContext context)
at Microsoft.Extensions.Internal.PropertyActivator`1.Activate(Object instance, TContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.Activate(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderPageAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Hs.Core.Web.Razor.DefaultRazorPage`1.RenderPartial[T](String partialViewName, T model) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 203
at Hs.Core.Web.Razor.DefaultRazorPage`1.<RenderZones>b__33_0(ZoneRegistration reg) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 179
at ServiceStack.EnumerableExtensions.Each[T](IEnumerable`1 values, Action`1 action) in /opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Common/EnumerableExtensions.cs:line 28
at Hs.Core.Web.Razor.DefaultRazorPage`1.RenderZones(List`1 registrations) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 160
at Hs.Core.Web.Razor.DefaultRazorPage`1.Zone(String zoneName) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 216
at AspNetCore._Views___Layout_cshtml.<ExecuteAsync>d__21.MoveNext() in /Views//_Layout.cshtml:line 53
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderPageAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderLayoutAsync>d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ServiceStack.Mvc.RazorFormat.RenderView(IRequest req, IResponse res, ViewDataDictionary viewData, IView view, String layout) in /opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Mvc/RazorFormat.cs:line 223
If i do as suggested above:
in _viewImports
@inherits DefaultRazorPage
Or
@inherits DefaultRazorPage<object>
And in the view:
@model MyModel
I Get an error property does not exist, exception:
One or more compilation failures occurred:
/Views/Shared/NavBar.cshtml(12,40): error CS1061: 'object' does not contain a definition for 'DebugMode' and no extension method 'DebugMode' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
Stack:
at Microsoft.AspNetCore.Mvc.Razor.Compilation.CompilationResult.EnsureSuccessful()
at Microsoft.AspNetCore.Mvc.Razor.Internal.CompilerCache.CreateCacheEntry(String relativePath, String normalizedPath, Func`2 compile)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.Internal.CompilerCache.GetOrAdd(String relativePath, Func`2 compile)
at Microsoft.AspNetCore.Mvc.Razor.Internal.DefaultRazorPageFactoryProvider.CreateFactory(String relativePath)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.CreateCacheResult(HashSet`1 expirationTokens, String relativePath, Boolean isMainPage)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.OnCacheMiss(ViewLocationExpanderContext expanderContext, ViewLocationCacheKey cacheKey)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.LocatePageFromViewLocations(ActionContext actionContext, String pageName, Boolean isMainPage)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.FindView(ActionContext context, String viewName, Boolean isMainPage)
at Hs.Core.Web.Razor.DefaultRazorPage`1.RenderPartial[T](String partialViewName, T model) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 193
at Hs.Core.Web.Razor.DefaultRazorPage`1.<RenderZones>b__33_0(ZoneRegistration reg) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 179
at ServiceStack.EnumerableExtensions.Each[T](IEnumerable`1 values, Action`1 action) in /opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Common/EnumerableExtensions.cs:line 28
at Hs.Core.Web.Razor.DefaultRazorPage`1.RenderZones(List`1 registrations) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 160
at Hs.Core.Web.Razor.DefaultRazorPage`1.Zone(String zoneName) in C:\Git\trancon.cartwiseng\Frontend\Source\Hs.Frontend.SelfHost\Hs.Frontend.Core.Web\Razor\DefaultRazorPage.cs:line 216
at AspNetCore._Views___Layout_cshtml.<ExecuteAsync>d__21.MoveNext() in /Views//_Layout.cshtml:line 86
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderPageAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderLayoutAsync>d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.<RenderAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ServiceStack.Mvc.RazorFormat.RenderView(IRequest req, IResponse res, ViewDataDictionary viewData, IView view, String layout) in /opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Mvc/RazorFormat.cs:line 223
@joelharkes The recommendation here is to specify @inherits ViewPage<TModel> in your _ViewImports.cshtml, e.g. here's the _ViewImports.cshtml used from https://github.com/NetCoreApps/RazorRockstars/
@inherits ViewPage<TModel>
@using ServiceStack
@using ServiceStack.Mvc
@using ServiceStack.Text
@using RazorRockstars
@using RazorRockstars
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@mythz im trying it now, why did you put this file in the wwwroot instead of Views folder?
Thanks mythz, its totally working, but ya resharper is still broken
It's unclear based on this, and the fact that the issue is closed, what the future direction is.
The inability to have custom Razor Views inherit from RazorPage\ While I understand the use of _ViewImports.cshtml and model, and I confirmed that it works, it is at best a workaround. And may be usable for trivial or average MVC apps. MVC5 was able to inherit from WebViewPage\ The corresponding view would look like this (note, no model required (or even allowed)). This custom class MyRazorView\ MVC6's lack of support for inheriting from RazorPage\ Obviously it's not possible to use inherits and model both in the same view. (Error: The 'inherits' keyword is not allowed when a 'model' keyword is used.) Further, in MVC5 the Html property was a properly typed HtmlHelper\ While I appreciate the new features and enhancements in MVC6, this is an area that was severely clipped in MVC6 and makes upgrading to MVC6 really unnecessarily difficult. Maybe there is a solution to somehow add typed properties to a view and my inexperience with MVC6 is preventing me from finding another solution, other than custom RazorPage\public class MyRazorView<TModule, TModel> : WebViewPage<TModel>
where TModule : ModuleDefinition
where TModel : class {
public RazorView();
...
public TModel Model { get; }
public TModule Module { get; }
...
}
@inherits MyRazorView<TextModule, TextModelDisplay>
@Html.DisplayFor(m => Module.Contents)
And the model must now be explicitly listed in the view. So now we define the class outside the view in _ViewImports.cshtml and the model in the view. (ugly?).
This was correct in MVC5 because WebViewPage\
Allowing inherits and model in the same view in MVC6 would seem an OK solution, although somewhat hacky... as both the inherits and model would specify the model type. At least it would eliminate the unnecessarily awkward separation of class and model definition.Property 'ViewData' is of type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[[YetaWF.Modules.Text.Controllers.TextModuleController+TextModelDisplay, YetaWF.Text, Version=1.1.1.0, Culture=neutral, PublicKeyToken=null]]', but this method requires a value of type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
Parameter name: viewContext
/cc @pranavkm
Is this still an issue for anyone? Or did anyone find issues with the workaround? I'm about to try this myself. Just thought I'd post this inquiry in the mean time (to find things to watch out for).
I've just run into this issue. Using _ViewImports.cshtml is a work-around so the issue still needs resolving. Can this be re-opened?
Our use-case is that we use a handful of razor base page types to expose functionality in views and templates used by our CMS. We have docs that details this usage if you need some background (generic view helper, page templates, page module types, custom entity details pages).
The workaround is limiting and non-intuitive in that:
We're trying to port to asp.net core, and so to get this working I've had to use the work around suggested here, but I've also had to remove generic type constraints on the on the razor base classes as this now causes issues:
An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
Generated Code
The type 'dynamic' cannot be used as type parameter 'TModel' in the generic type or method 'CofoundryTemplatePage<TModel>'. There is no implicit reference conversion from 'dynamic' to 'Cofoundry.Web.IEditablePageViewModel'.
-
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System.Threading.Tasks;
public class _Cofoundry_PageTemplates__ViewStart_cshtml : CofoundryTemplatePage<dynamic>
{
#line hidden
public _Cofoundry_PageTemplates__ViewStart_cshtml()
{
}
#line hidden
We may end up changing our approach, but it's difficult to see how we can achieve the same thing through other razor features. We make use of the model and the view context in our template helpers and I haven't seen a way to inject this into an injected service yet.
@YetaWF \ @HeyJoel - I'm moving your issue to a separate work item. The original work issue was based on being able to specify a base type and a model and that works with _ViewImports. However clearly the implementation has issues and doesn't work in the scenarios you listed. I filed separate work items for these so they can be triaged and tracked:
The problem is putting the imports in the ViewImports file is that you are making the assumption that all my pages are going to inherit that page, and due to the folder structure I would have to put a ViewImports in every views folder, that's not very efficient or intuitive