Online courses in ASP.NET MVC, ASP.NET Core, and Design Patterns conducted by Bipin Joshi. Read more...
Learn ASP.NET MVC / Core, jQuery, Angular, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

Working with Virtual Path Providers

Introduction

In the majority of the web sites web form exist physically on the web server. However, in some cases you may want to deviate from this storage mechanism. For example, some Content Management Systems (CMS) allow the end users to add web forms dynamically. In such cases disk based storage is tedious to manage. More elegant approach would be to store the web forms in a database and serve them as and when requested. But how can we accomplish this? ASP.NET Virtual Path Providers (VPP) is the answer. In this article I will illustrate how a simple VPP can be developed and plugged in your web site.

What are Virtual Path Providers?

Normally whenever you submit a request for a web form, IIS and ASP.NET assume that the requested web form (.aspx) exists physically on the hard disk. Virtual Path Providers (VPP) allow you to deviate from this default assumption. Using VPP you can serve the request for a web form from a database or any other storage mechanism. This way the web form need not exist physically on the web server. VPP approach is best when you have a part of your web site that requires web forms generated dynamically. If you are thinking of using VPP as a substitute for the traditional disk based storage then be careful to evaluate the performance penalty involved.

The System.Web.Hosting namespace provides three important classes viz. VirtualPathProvider, VirtualFile and VirtualDirectory. Together these classes allow you retrieve web forms stored in any location. In the following example I will show you how this can be done.

Creating a VirtualFile

The virtual file is a class that represents a requested file (typically a web form). In order to create a virtual file you need to write a class that inherits from VirtualFile base class. The following code shows an example.

public class DbVirtualFile:VirtualFile
{
...
}

Here, we created a class named DbVirtualFile that inherits from VirtualFile as the base class. The constructor of the VirtualFile base class accepts virtual path of the file being requested as a parameter. Hence, your class (DbVirtualFile) needs to pass this parameter to the base class. The following code shows how this can be done.

public DbVirtualFile(string virtualpath)
:base(virtualpath)
{
string strConn = ConfigurationManager.ConnectionStrings
["connstr"].ConnectionString;
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "select webformcontent from webforms 
where virtualpath='" + virtualpath + "'";
object retval = cmd.ExecuteScalar();
cnn.Close();
if (retval != null)
{
WebFormContent = retval.ToString();
}
}

The constructor of DbVirtualFile class accepts virtual path of the file being requested as a parameter. This parameter is then passed to the base class VirtualFile. The constructor then reads the web form content from the WebForms table (see code download for the database). The public property WebFormContent is then set to the content of the web form we just retrieved.

Now comes the important part. The DbVirtualFile class should override the Open() method of the VirtualFile base class and return the content of the web form as a Stream. This way ASP.NET need not know from where the web form was served. The overridden Open() method is given below:

public override Stream Open()
{
MemoryStream ms = new MemoryStream();
if (WebFormContent != null)
{
byte[] data = ASCIIEncoding.ASCII.
GetBytes(WebFormContent);
ms.Write(data, 0, data.Length);
}
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
return ms;
}

The return type of the Open() method is Stream. This abstraction allows us to return any type of stream (MemoryStream, FileStream etc.) from the method. In our example we create an instance of MemoryStream. We then write the contents of the web form into it. Before returning the MemoryStream we take its read pointer to the beginning. Failing to do so can cause unexpected results (a blank page for example).

This completes our implementation of virtual file. Now it's time to develop VirtualPathProvider that makes use of DbVirtualFile class.

Creating a VirtualPathProvider

In order to create your own VirtualPathProvider you need to create a class that inherits from VirtualPathProvider base class (see below).

public class DbVirtualPathProvider:VirtualPathProvider
{
...
}    

Then add a static method named AppInitialize() to the DbVirtualPathProvider class. This method  is called automatically by the ASP.NET framework and it registers your custom virtual path provider (DbVirtualPathProvider in the above example) with the ASP.NET framework.

public static void AppInitialize()
{
DbVirtualPathProvider db = new DbVirtualPathProvider();
HostingEnvironment.RegisterVirtualPathProvider(db);
}

The AppInitialize() method simply creates an instance of DbVirtualPathProvider class and registers it with ASP.NET framework by calling RegisterVirtualPathProvider() method of the HostingEnvironment class. This is how ASP.NET framework becomes aware of our virtual path provider.

Next, you need to override two methods of the VirtualPathProvider base class namely FileExists() and GetFile(). The former method returns a Boolean value indicating whether the requested file exists or not. The later method returns a VirtualFile from the underlying storage mechanism (database in our case). These methods are shown next.

public override bool FileExists
(string virtualPath)
{
string strConn = ConfigurationManager.
ConnectionStrings["connstr"].ConnectionString;
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "select count(*) from webforms 
where virtualpath='" + virtualPath + "'";
object retval = cmd.ExecuteScalar();
cnn.Close();
int i = Convert.ToInt32(retval);
if (i <= 0)
{
return false;
}
else
{
return true;
}
}

The FileExists() method simply checks if the requested file exists in the WebForms table. Notice that the FileExists() method receives the virtual path as a parameter.

public override VirtualFile GetFile
(string virtualPath)
{
DbVirtualFile file = new DbVirtualFile
(virtualPath);
if (file.WebFormContent == null)
{
return Previous.GetFile(virtualPath);
}
else
{
return file;
}
}

The GetFile() method simply constructs a new instance of DbVirtualFile class. It then checks if the WebFormContent property has anything and accordingly returns the DbVirtualFile instance back. Notice the use of Previous inbuilt property. This property gives a reference to the previously registered VirtualPathProvider (i.e. the default one in our case). This way if the requested file doesn't exists in the database ASP.NET will try to get it from physical file system.

That's it! We have just completed our virtual path provider and are ready to test our web site. Open the WebForms table in Server Explorer and add an entry for Test.aspx

Notice that the virtual path of Test.aspx also includes its application name (VirtualPathProviderDemo). Then run the web site and manually navigate to Test.aspx. You should get the results as shown below.

Observe that Test.aspx doesn't exist physically but because of our virtual path provider ASP.NET is able to serve its contents.




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 26 May 2008



Tags : ASP.NET Web Forms Custom Controls Components