Razor Class Library with ASP.NET Core MVC

ASP.NET Core 2.1 introduces Razor Class Library (RCL). It’s the new way how to share reusable web application components with UI as dependent project or NuGet package. As public examples mostly cover how it works with Razor Pages, I decided to write this blog post and demo application for ASP.NET Core MVC.

Source code available! I created demo application that shows all stuff written here. Feel free to visit my GitHub repository gpeipman/RazorClassLibraryDemo. To make it work it’s important to have stable release of .NET Core 2.1 without any preview or release candidate releases.

What is Razor Class Library?

Razor Class Library is ASP.NET Core library that contains pages, views, view components and other web application artifacts with their respective code. Library doesn’t run independent – there must be application that includes library and uses its functionalities. Razor class libraries make it easy to share web applications code with UI elements.

Razor UI Library project

Razor class libraries doesn’t work well on preview and release candidate versions of ASP.NET Core 2.1.

Razor class library structureRazor class libraries are similar to ASP.NET Core web applications by structure. It is recommended to put custom UI pieces to areas and it is up to developers to make sure that the names of areas doesn’t match.

Screenshot on right shows default structure. For some reason the project comes with Razor page. There is nothing about MVC and there’s also no project template for Razor class library with MVC structure.

But MVC is actually supported and it’s up to us to build the project structure we need.

Referencing Razor class library

There are two ways to reference Razor class library:

  • Project reference
  • NuGet package

If class library is in same solution with web application then project reference is enough.

Reference to Razor UI library

Another important dependency is project dependency under build dependencies menu (right click on web application and select Build dependencies).

Project dependency to Razor UI library

Setting this dependency tells Visual Studio that library project must be built before web application one.

Moving to MVC

Razor UI library with MVC structureNow we have everything prepared and it’s time to move to MVC. Screenshot on right shows the minimal structure we need to get things running. From MyFeature folder remove all Razor pages things that were created automatically and create simple MVC folder structure with HomeController and Index view.

There are some things to know:

  • Controllers must have AreaAttribute
  • Web application must have area route defined

Now let’s implement these changes and let’s make things work.

We start with HomeController in library project. I started with minimal and extremely primitive code.

[Area("MyFeature")]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Same goes for Index view of HomeController.

<h1>Hello from MyFeature</h1>

In Startup class of web application include MVC like show here (Configure method).

app.UseMvc(routes =>
{
    routes.MapRoute("areas", "{area}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});

Now we are ready to run our application and try out view from Razor class library.

MVC view from Razor UI library

Hold on, what the hell is this? Well… we have area defined in another project and what’s going on in web application doesn’t apply to it fully. We need to copy _ViewStart.cshtml file under Shared folder in Razor class library. After this things get better.

Razor UI library MVC view with layout

This is the easiest method to use and it works well for applications with only one layout.

Overriding Razor class library views

It’s possible we want to use our own view instead of one that comes with Razor classlibrary.

Overriding Razor UI library viewFor this we need to build exactly the same area structure as we built in Razor class library but we build it in our web application. We don’t need any copies of controllers or models in Razor class library project, just views we need to override.

It’s possible to override also controllers the same way but this is more complex topic and it needs some good analyzis before doing so. The question comes down to simple thing – is library author expecting something like this to happen?

Image on right shows how I replicated areas structure in Razor class library above to web application. Notice how there is only the index view of HomeController that I want to override.

Here is the primitive content of overriden view.

<h1>Hello from overridden MyFeature</h1>

And here is our overridden view in action.

Razor UI library view overridden by local view

If needed we can override views but we have to be careful – views from Razor class library may contain logic we don’t know.

Wrapping up

Razor Class Library is excellent way how to share web application components and UI artifacts. It’s not just about sharing components between projects in same solution – we can share our Razor class libraries as NuGet packages. The model seems flexible enough for me as it easy to take risks of overriding controllers, models, views and other components. I don’t give any suggestion right now about overriding as I am not yet sure what weight it actually puts to those responsible of Razor class libraries. Anyway, with Razor class libraries one problem is solved again.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    16 thoughts on “Razor Class Library with ASP.NET Core MVC

    • Pingback:The Morning Brew - Chris Alcock » The Morning Brew #2598

    • June 6, 2018 at 4:05 am
      Permalink

      “Razor class libraries doesn’t work well on preview and release candidate versions of ASP.NET Core 2.1.”

      “It is recommended to put custom UI pieces to areas and it is up to developers to make sure that the names of areas doesn’t match.”

      Is the meaning of these 2 sentences intended?

    • June 6, 2018 at 10:10 am
      Permalink

      Yes, of course.

      1. I tried to run my sample on ASP.NET Core 2.1 preview and release candidate. On both there were issues with finding views for controllers in RCL. With release version everything works fine.

      2. There’s no central repository of area names and it’s impossible to know for public packages if some area name is taken or not. I’m trying to find some nice solution for naming conflicts.

    • Pingback:Razor Class Library with ASP.NET Core MVC - How to Code .NET

    • Pingback:ASP.NET Core Identity scaffolding | Gunnar Peipman - Programming Blog

    • August 1, 2018 at 12:30 pm
      Permalink

      Great information. Since last week, I am gathering details about asp experience. There are some amazing details on your blog which I didn’t know. Thanks.

    • September 5, 2018 at 7:00 pm
      Permalink

      Thanks a lot

    • September 6, 2018 at 10:54 am
      Permalink

      Is there a way to store static content (JavaScript, CSS) in a RCL?

    • September 11, 2018 at 7:12 am
      Permalink

      AFAIK there’s no straightforward way include static files. It’s possible to embed these files as resources and then use controller to extract them.

      Example can be found here: https://github.com/aspnet/Docs/issues/7662

      public class MiniCMSContentController : Controller
      {
      static Dictionary mime = new Dictionary {
      { “.js”, “text/javascript” },
      { “.css”, “text/css” },
      };

      [Route(“/MiniCMS/Content/{filename}”)]
      public IActionResult Index(string filename)
      {
      string outMime = “application/unknown”;
      mime.TryGetValue(Path.GetExtension(filename).ToLower(), out outMime);
      return new FileStreamResult(Assembly.GetExecutingAssembly().GetManifestResourceStream(“MiniCMS.Content.” + filename), outMime);
      }
      }

      I have not tried this approach out in practice and cannot tell you if there are hidden issues or not.

    • November 6, 2018 at 3:47 pm
      Permalink

      Very good article, thank you, saved me some time today. It seems the html helper tags does not work, need to figure this one out now.

    • November 6, 2018 at 3:57 pm
      Permalink

      Just add _ViewImports.cshtml to views as done with _ViewStart.cshtml

    • January 3, 2019 at 12:12 pm
      Permalink

      Nice Article,

      Works fine if the RCL is as a project added to the main project.
      However the moment I try to add it as a compiled assembly, I can’t seem to make it to work.

      I even loaded my 2 assemblies (razor and view) through ApplicationPartManager and ConfigureApplicationPartManager.

      I get some strange error
      Invalid operation: The model item passed into the ViewDataDictionary is of type ‘xxx.ViewModel’, but this ViewDataDictionary instance requires a model of type ‘xxx.ViewModel’

      Are you able to make that work as just an assembly ?

    • January 5, 2019 at 8:33 am
      Permalink

      Informative insights! I am new to ASP.Net programming. Your article has helped me understand its basics and learn more about razor pages. I am obliged for this informative article, keep sharing.

    • December 25, 2019 at 8:30 pm
      Permalink

      I have learnt a lot from this article.
      What is difference between RCL and other asp.net Non-RCL Library?

    • January 16, 2020 at 5:14 am
      Permalink

      Thanks for your article!
      It is a useful topic for people

    • March 27, 2022 at 1:15 pm
      Permalink

      I’m curious about something. In the article it says, “It’s possible to override also controllers the same way but this is more complex topic and it needs some good analyzis before doing so.” I haven’t been able to figure out how this would work. When I try to create a default set of controller actions in my RCL and then another set in the main project, I keep running into an AmbiguousActionException. Anyone have any thoughts on whether it is actually possible to override controllers the same way as views? Thanks!

    Leave a Reply

    Your email address will not be published. Required fields are marked *