It is quite common to predefine some namespaces to be available in the context of your Razor view files in ASP.NET MVC. In MVC 5, it was done inside the web.config file - not the “main” application one, but the one residing inside your Views folder.
Additionally, the same file was used to define the pageBaseType for your Razor views. This way you could expose extra members or behaviors to your pages, such as injected services or common configuration objects.
Since there is no more web.config in ASP.NET Core 1.0 MVC, let’s have a look at how to achieve the same in the next generation ASP.NET.
The ASP.NET MVC5 Way π
Here is a quick reminder of what you’d have to do in ASP.NET MVC5 to import some default namespaces into your views and set a custom base view page.
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="MyProject.Infrastructure.MyBasePage"> <namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="MyProject.Infrastructure" />
<add namespace="MyProject.Models" />
</namespaces> </pages> </system.web.webPages.razor>
This section would be added to your web.config (the one in the Views folder) and it allows you to import relevant namespaces into the views - in this case we added 2 custom ones, MyProject.Infrastructure and MyProject.Models.
On top of that, the class MyProject.Infrastructure.MyBasePage would be used as the base for all the views, meaning all of its non-private members would be accessible from the view.
This is quite useful as it allows you to share some common information or utilities required in the views (i.e. your custom application configuration object) without explicitly passing it into every view model. Moreover, this approach allows you to access the objects exposed this way also from layout files, which do not have a backing view model.
The ASP.NET Core MVC way π
In ASP.NET Core, we don’t put any application specific settings into web.config anymore. This doesn’t mean it may not exist - if you run on IIS you will have some sort of a basic web.config file, but it won’t be used by your application anymore, just by the server.
Instead, the framework introduces a new concept, a _ViewImports.cshtml file, which is similar - conceptually - to the familiar (from MVC 5) _ViewStart.cshtml. You can put it into your Views folder, or any of the subfolders inside it.
You can then define both the base page for your views and the namespaces to be imported into them from _ViewImports.cshtml. _ViewStart.cshtml still exists in ASP.NET Core and its purpose is unchanged - it can be used to set the layout file or execute arbitrary code.
Interestingly - and surely the early adopters felt the pain - throughout the different ASP.NET Core release (alpha/beta/RC) this functionality has taken different shapes and forms - at first you’d import namespaces directly in _ViewStart.cshtml, then this responsbility was moved into a file called _GlobalImport.cshtml, until it was renamed to _ViewImports.cshtml - which is where we are today.
Anyway, suppose you want to implicitly import, as we did in the earlier example, the namespaces MyProject.Infrastructure and MyProject.Models to all the views, to avoid having to manually import them in every view or having to fully qualify your types.
All you need to do is add the following lines to _ViewImports.cshtml:
@using MyProject.Models
@using MyProject.Infrastructure
In other words, just use the regular Razor syntax for namespace importing.
By default ASP.NET Core 1.0 MVC imports the following namespaces for you - all other ones must be manually imported:
- System
- System.Linq
- System.Collections.Generic
- Microsoft.AspNetCore.Mvc
- Microsoft.AspNetCore.Mvc.Rendering
In a similar fashion, we can use _ViewImports.cshtml to define the base view page. Our custom view page might look like this:
public abstract class MyBasePage<TModel> : RazorPage<TModel>
{
[RazorInject]
public IMyService MyService { get; set; }
public MyConfiguration Conf => new MyConfiguration();
}
With this custom base view page, we are exposing an instance of MyConfiguration object via an ambient Conf property which will be exposed to all of the views.
Additionally, we also a expose an instance of IMyService as another property: MyService. However, this one is a bit more interesting - instead of being instantiated directly in the page class, it will be injected from the ASP.NET Core DI, because we used the new RazorInject attribute on the property, which instructs the framework to hydrate this dependency from the DI container.
This is a neat improvement over using base view pages in MVC 5, where it was fairly common to resolve these type of dependencies from the static service locator (DependencyResolver).
And here is our updated _ViewImports.cshtml:
@inherits MyBasePage<TModel>
@using MyProject.Models
@using MyProject.Infrastructure
So it is now valid in a Razor page to use the following code (the methods and properties on IMyService and MyConfiguration are obviously made up for illustration purposes):
@MyService.GetName()
][1]{.btn.btn-default.btn-default}
One more thing worth mentioning is that the location of your _ViewImports.cshtml file matters. If it’s placed directly under Views folder, it will apply to all of the Razor views in that folder and all of the lower level folders (so in most cases - to all of the views of your application).
However, if you put it inside any of the folders located under the Views folder, say Views/Home it will only apply to the views there and not affect any of the sibling/parent level folders.
If you define a base view page at a top level (directly under Views), and then define another one again at a deeper level (i.e. Views/Home), the latter will take precedence. This gives you ability to have granular control over what’s exposed to views and where.
Finally, _ViewImports.cshtml also allows you to inject dependencies into views directly. For example, the following syntax can be used to inject IMyService into all the views:
@inject MyProject.Infrastructure.IMyService MyService
This would make IMyService be resolved from ASP.NET Core DI, and exposed in the views as MyService property of the base view page.
This is semantically equivalent to what we did earlier - having the base view page defined manually, and using RazorInject attribute to resolve this dependency, so it’s up to you to use the approach you feel more comfortable with.
Finally, there are some cool and interesting hacks we can do around the entire Razor code generation experience (using Roslyn) but let’s skip this for now, and wait for ASP.NET Core RC2 before diving deep into that π