:::: MENU ::::

Monday, January 18, 2016

The other day we explored using view components in ASP.NET 5 – as a very nifty replacement for the old MVC ChildActions. View components allow you to package a piece of functionality into a reusable class, along with an accompanying view, that can be invoke from any other view on demand.
Today let’s take this a step further – and let’s see how you can configure ASP.NET MVC 6, to be able to consume view components not just from the current web project but from external sources – external assemblies too. This way you will be able to share and distribute your view components across multiple projects. This is definitely useful for anyone who has – for example – ever worked on a portal-style applications, where building reusable components is one of the most important development activities.

The requirements

While the latest version of ASP.NET 5 at the time of writing is RC1, this particular feature that I am going to be using is only available in the latest code (which is going to become RC2 in some – near? – future). For now, in order to be able to follow along, we’ll be pulling the packages from the CI feed at https://www.myget.org/F/aspnetcidev/api/v3/index.json. You can either modify your machine nuget packages sources (although that’s a bit brave), or just add a nuget.config file, pointing to the CI feed, to your solution root:
By the way, all of the source code for this article is available at Github.

Set up

In order to be able to use view components from external assemblies we will rely on a feature called file providers – represented in ASP.NET 5 by an interface IFileProvider. It allows you to instruct ASP.NET 5 from where the files (such as for example MVC views) that should be used within the application are coming from. RoslynCompilationService, when compiling your application, is going to rely on the file provider to discover files.
By default, to find Razor views, ASP.NET MVC 6 preregisters the PhysicalFileProvider, an implementation of IFileProvider that looks at all the files in the base path of your application.
In MVC 6, since RC2 RazorViewEngineOptions, which can be configured at your MVC application startup, will enable you to set multiple IFileProviders that the MVC framework is going to consult (in addition to the default PhysicalFileProvider, or even instead of it – since it can be removed too) when looking for files. At this point it should be pretty self explanatory – normally MVC 6 would only look at the current project to discover views. With the possibility to register extra providers, you can instruct the framework to additionally look at an external assembly and consider the embedded resources from there too.
What happens under the hood, is that the framework actually relies on a new implementation of IFileProvider, calledCompositeFileProvider (also introduced in RC2), which is simply able to wrap multiple IFileProviders, and expose the files provided by them using a single common interface.
As mentioned, this change was only introduced recently – if you tried this against the RC1 version of the code, you’d have noticed that only a single IFileProvider was allowed for an MVC application in that version. That obviously made it impossible to efficiently share things such as view components. After all, if you chose an external file provider – pointed it to external assembly – the framework would no longer discover your “web project” views anymore as it would replace the default PhysicalFileProvider.

Code

So let’s start by creating an empty ASP.NET 5 application. I am going to reference all of the dependencies as latest – and they will come from the CI feed as pre release RC2 builds.
My project.json is shown below. The application is called Reusable.WebApp and it references a class library, created in the same solution, called Reusable.Components.
We are also referencing Microsoft.AspNet.FileProviders.Embedded package, but more on that later.
Our reusable view component is going to something very simple – something that we already built in the old post. Of course no one wants to read old posts, so here is the C# code:
And the corresponidng Razor view, located under /Views/Shared/Components/PromotedProducts/Default.cshtml.
As mentioned, the difference is that instead of adding all that code into an ASP.NET Web Application, we are actually going to add it to a class library instead (that Reusable.Components project I mentioned earlier).
This class library needs to reference Microsoft.AspNet.Mvc package in the project.json in order to be able to contain a class inherting from ViewComponent, otherwise it wouldn’t compile, but that’s about it – it’s not a web project by any means.
Now, in order for the Razor views to be embedded as resources inside this library, we need an additional node in the project.json, called resources where we’ll specify the path to our library-specific views. This is shown in the next listing, illustrating theproject.json of that class library:
Note that since we are using the CI feed, and every package involved is latest and greatest, the target framework moniker needs to be dotnet5.5 not dotnet5.4 anymore, but that’s just a detail.
Finally, to stitch it all together we need to instruct ASP.NET MVC 6 to use our external components by adding anEmbeddedFileProvider, pointing to our Reusable.Components class library as one of the available file providers in the web application. This is done at application startup, inside the ConfigureServices method of the Startup class.
Because the FileProviders on the RazorViewEngineOptions is now (in RC2) a collection, we can now inject an extra provider without having to worry about losing the default PhysicalFileProvider.
And that’s about it. You can now add the view component (a call to @await Component.InvokeAsync(“PromotedProducts”)) to any page in your application and use as if it was locally present inside the web app. There are surely plenty of great uses cases of being able to package reusable components this way and use them in various web applications.
As a reminder, all of the source code for this article is available at Github

Footnote

You can also use this approach in ASP.NET MVC 6 RC1 if you just pull in a CI version (so RC2-compatible) ofMicrosoft.AspNet.FileProviders.Composite package. It will contain a CompositeFileProvider, which will let you to bundle together the default file provider (PhysicalFileProvider) and an external one such as an EmbeddedFileProvider pointing to your class library with reusabel components. Then you can simply set this instance of CompositeFileProvider on the FileProviderproperty of RazorViewEngineOptions. The reason for this is that – as mentioned already – MVC 6 RC1 had only a single file provider, while MVC 6 RC2 allows multiple – hence the difference in the apporach.