Writing flexible code in ASP.NET Core 2.0 Razor Pages
In a previous article I demonstrated how to write flexible code[^] for n-tier designed applications. In this article, I want to describe how I approached designing my code for our ASP.NET Core 2.0 Razor Pages application. My key goal was to separate out the various concerns, and in particular keep the UI code separate from the business logic code.
We are using Razor Pages in our current app, and all the business logic is encapsulated within our ASP.NET Web API services which are invoked by the Razor Pages. A Razor Page is backed by a PageModel class which supplies much of the “plumbing” logic behind the Razor Page. For example, the PageModel class contains such things as the Response, the Request, ViewData, PageContext, HttpContext. But no business logic. So this article will describe how I have approached surfacing business logic within my Razor Pages.
It is worth noting that I have deliberately used a very simple example for clarity and to keep the article nice and simple.
The first thing I did was to create a base PageModel class for the Razor Pages. As stated earlier, all Razor Pages are backed by a PageModel class as in the following code.
Hide Copy Code
public class IndexModel : PageModel
{
//rest of code goes here
}
So I created a base PageModel class that I would use instead. My base PageModel class inherits from the default AspNetCore.Mvc.RazorPages but adds the ability to specify a completely different class which will contain all the business logic.
Hide Copy Code
public class PageModelBase<T>: PageModel where T : PageModelService, new()
{
public readonly T Service = new T();
}
I wanted consistency in my design, so I created a base service class that would be instantiated by the PageModel classes. I have called this class PageModelService class. In the example code above, I am creating an instance of this backing service class in my PageModel. The PageModelService class is where I will place all my business logic code (which in my case are my ASP.NET Web APIservices). This separation ensures that the business logic code is separated out from the UI code, and is therefore also unit-testable.
Here’s my PageModelService class definition.
Hide Copy Code
public abstract class PageModelService
{
protected abstract string ModuleName { get; }
}
I only have one property defined (the name of the module to which the Razor Page belongs), but you can define as many properties, methods etc as your applications needs. Remember, this is the base PageModelService class, so only place code here that is applicable to all your Razor Pages.
Here’s an example class definition for a PageModelService that sets the ModuleName property in the constructor.
Hide Copy Code
public class ExamplePageModelService : PageModelService
{
public ExamplePageModelService ()
{
ModuleName = "Example Module;
}
protected override string ModuleName { get; }
}
Finally, here’s a Razor Page using the new PageModelBase class and setting the name of the backing service (which will be instantiated by the class).
Hide Copy Code
public class ExamplePageModel : PageModelBase<ExamplePageModelService>
{
public OnPost()
{ } public void OnGet()
{
}
}
This very simple design pattern allows us to separate the UI code from the business logic code within the Razor Pages and allows the business logic code to be unit tested also. So if you’re building Razor Pages and want to keep your UI code separate from your business logic code, then give this design pattern a try. You are encouraged to amend the code to suit your own specific requirements, but feel free to use the code as a starting point.