X

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 class libraries doesn’t work well on preview and release candidate versions of ASP.NET Core 2.1.

Razor 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.

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

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

Moving to MVC

Now 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.

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.

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.

For 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.

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.

Liked this post? Empower your friends by sharing it!
Categories: ASP.NET

View Comments (13)

  • "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?

  • 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.

  • 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.

  • 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.

  • 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.

  • 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 ?

  • 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.

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

  • 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!

Related Post