Load Views From Database in ASP.NET MVC
ASP.NET MVC organizes view files in the Views folder. This arrangement works
fine for most of the applications. However, in some cases you may need to load
views from database rather than physical disk file. Consider an example wherein
you are building a portal or a content management system. You may want to allow
the administrator to create or modify views and then you may want to load these
views dynamically. Such dynamically created / modified views may not be a nice
fit into the folder based arrangement of MVC. In such cases the recommended way
is to store views in a database and load them on the fly. This article discusses
how t his can be accomplished.
The Views database table
In order to load views from a database you first need to create a table (I
have named it - Views) as shown below:

You can add the Views table in any existing database or create a database in
App_Data specifically to house Views table. The Views table has four columns -
Id, ViewName, ViewPath and ViewContent. The ViewName column stores a developer
friendly name of a view. This name is purely for your own use and
identification. The ViewPath column stores the path of a view from the root
folder. For example, /Views/Home/Index.cshtml. Note that ViewPath is not a ~ qualified
path. This simplifies your code that checks existence of a view (you will see
that later). The ViewContent column holds the actual content of a view. This can
be HTML markup and / or Razor code.
Entity Framework data model to read view data
Next, you need to have some way to read the data stored in the Views table
shown above. You can write a POCO using plain ADO.NET or use EF model to do
that. I my example I am using EF generated model. You also need to add a model
class for the Employees table of the Northwind database. Again, this can be a
POCO or EF designer generated class. I am using EF designer generated class. The
two EF entities mentioned above are shown in the following figure:

Controller and view content
Now, add Home controller to the Controllers folder and modify its Index()
action method as follows:
public ActionResult Index()
{
NorthwindEntities db=new NorthwindEntities();
List<Employee> model = db.Employees.ToList();
ViewBag.Message = "This view is loaded from database!";
return View(model);
}
The Index() action simply pulls all the Employee records into a List and
passes that List to the view. It also sets Message property on the ViewBag. The
Message property has been added just for the sake of testing.
Then add Index view to the project and modify it as shown below:
@model List<ViewsInDbDemo.Models.Employee>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<h1>List of Employees</h1>
<h2>@ViewBag.Message</h2>
<table border="1" cellpadding="10">
@foreach(var item in Model)
{
<tr>
<td>@item.EmployeeID</td>
<td>@item.FirstName</td>
<td>@item.LastName</td>
</tr>
}
</table>
</body>
</html>
The Index view is quite straightforward and simply outputs the model data
(Employees) and ViewBag message (Message) to the response stream. You can run
the application and see whether the view displays the Employee data and Message
as expected.
Now comes the important part! Copy this whole view content and paste
it into the ViewContent column of the Views table that you created earlier. And
then DELETE this Index.cshtml file. That's because we want to load views from
database and not from physical disk files. Also set the ViewPath column to
/Views/Home/Index.cshtml.
Creating Custom VirtualPathProvider and VirtualFile
The key part of our solution is the creation of a custom VirtualPathProvider
class and a VirtualFile class. A VirtualPathProvider does the job of picking the
views from disk, database or any other location. In our case it will pick views
from database. The views picked from database are wrapped in a custom
VirtualFile so that MVC framework can further deal with it.
To create a VirtualPathProvider class, add a new class to the project as
shown below:
public class BinaryIntellectVirtualPathProvider : VirtualPathProvider
{
public override bool FileExists(string virtualPath)
{
var view = GetViewFromDatabase(virtualPath);
if (view == null)
{
return base.FileExists(virtualPath);
}
else
{
return true;
}
}
public override VirtualFile GetFile(string virtualPath)
{
var view = GetViewFromDatabase(virtualPath);
if (view == null)
{
return base.GetFile(virtualPath);
}
else
{
byte[] content = ASCIIEncoding.ASCII.
GetBytes(view.ViewContent);
return new BinaryIntellectVirtualFile
(virtualPath,content);
}
}
public override CacheDependency GetCacheDependency
(string virtualPath,Enumerable virtualPathDependencies,
DateTime utcStart)
{
var view = GetViewFromDatabase(virtualPath);
if (view !=null)
{
return null;
}
return Previous.GetCacheDependency(virtualPath,
virtualPathDependencies, utcStart);
}
private View GetViewFromDatabase(string virtualPath)
{
virtualPath = virtualPath.Replace("~", "");
ViewsDbEntities db = new ViewsDbEntities();
var view = from v in db.Views
where v.ViewPath == virtualPath
select v;
return view.SingleOrDefault();
}
}
The BinaryIntellectVirtualPathProvider class inherits from
VirtualPathProvider base class. The VirtualPathProvider class resides in
System.Web.Hosting namespace. It then overrides a few methods of the base class
namely FileExists() and GetFile(). There is also a helper method
GetViewFromDatabase().
The GetViewFromDatabase() method retrieves a requested view from the database
(Views table) and returns it to the caller. The same method can be used to
determine whether a view exists in the database or not. For example, if
GetViewFromDatabase() returns null it indicates that a requested view doesn't
exist in the Views table.
The FileExists() overridden method checks whether a view exists in the
database. If it doesn't then it calls the FileExists() of the base class,
otherwise it returns true.
The GetFile() overridden method does the job of retrieving a view from the
database and returning it to the caller. The method first checks whether the
requested view exists in the Views table. If it doesn't then it calls the
GetFile() method of the base class. If the view exists in the database it
creates a new instance of BinaryIntellectVirtualFile class (discussed
shortly). The virtual path and the view content (ViewContent column) are passed
to the constructor of the BinaryIntellectVirtualFile class. Notice that the view
content is passed after converting it into a byte array. This is because
BinaryIntellectVirtualFile class writes this content into a MemoryStream and
MemortStream requires its content as byte array.
Now add a custom VirtualFile class class as shown below:
public class BinaryIntellectVirtualFile : VirtualFile
{
private byte[] viewContent;
public BinaryIntellectVirtualFile(string virtualPath,
byte[] viewContent) : base(virtualPath)
{
this.viewContent = viewContent;
}
public override Stream Open()
{
return new MemoryStream(viewContent);
}
}
The BinaryIntellectVirtualFile class inherits from VirtualFile base class (System.Web.Hosting)
and overrides Open() method. Notice that the constructor of
BinaryIntellectVirtualFile receives view content as a byte array. The Open()
method constructs a new MemoryStream based on this byte array and returns the
MemoryStream to the caller.
Register the VirtualPathProvider
The final step is to register the custom VirtualPathProvider with the MVC
framework. You do this in Global.asax as shown below:
protected void Application_Start()
{
...
HostingEnvironment.RegisterVirtualPathProvider
(new BinaryIntellectVirtualPathProvider());
}
To register a custom VirtualPathProvider you use HostingEnvironment class
from System.Web.Hosting namespace. The RegisterVirtualPathProvider() method
accepts a new instance of a VirtualPathProvider class (BinaryIntellectVirtualPathProvider
in my example).
You can run the application and see the view loaded from database in
action. Here is a sample run:

That's it for now! Keep coding.