Building an Online Storage System in ASP.NET 2.0
page 5 of 7
by Xianzhong Zhu
Feedback
Average Rating: 
Views (Total / Last 10 Days): 34393/ 117

Dissecting the solution

We will start the dissection by looking at the Interface design for this application.

Interface design

The main component in this sample online storage system is an interfaceIDisk which declares nearly all the underlying methods associated with folders and files, such as method GetAllDirectoryFile() for getting information of sub folders and files, method GetFiles() for obtaining all the related files. The following gives us the class diagram of interface IDisk.

Figure 4

As shown in Figure 4, we encapsulate all the basic file and folder operation-related declarations into a single interface, which is a good practice in real life. Since all the modules are small and basic, and even self-explanatory, we do not dwell too much on them here; we will clarify some of them in the next section and in later use.

Data tier design

The data access tier of the whole system mainly relies on one class, Disk, which is derived from the interface IDisk depicted above and should implement all the methods declared in it. The sketch in Figure 5 gives us a more intuitive depiction of the relationships between the modules we will use.

Figure 5

Besides all the methods in interface IDisk, class Disk defines its own two methodsShowDirectory(DropDownList dirList, int nParent) and CreateChildNode(DropDownList dirList, DataTable dataTable, int nParent, string sParentName). The two methods are cooperatively responsible for displaying folder information according to the corresponding folder hierarchy. To achieve this goal, we take the steps as follows.

1.    Obtain folders data from inside the database and store up the results via a object dataTable.

2.    Append the root folder info into the DropDownList dirList.

3.    Retrieve all the sub folder info whose folder ID is nParent and add it into the control dirList by invoking method CreateChildNode.

4.    Repeat step 3 until all the related sub folders are added into the control dirList.

In the above flow, steps 1-3 mainly depend on method ShowDirectory, while step 4 is accomplished through method CreateChildNode. Below are their corresponding codes.

Listing 1: Code for the two own functions of class Disk

public void ShowDirectory(DropDownList dirList,int nParentID)
      {
            DataTable dataTable = SystemTools.ConvertDataReaderToDataTable(GetDirectorys());
            dirList.Items.Clear();     ///clear all the nodes
 
            DataRow[] rowList = dataTable.Select("ParentID='-1'");
            if(rowList.Length <= 0) return;
 
            ///Create and add a root node
            dirList.Items.Add(new ListItem("/",rowList[0]["DirID"].ToString()));
 
            ///Create other nodes
            CreateChildNode(dirList,dataTable,Int32.Parse(rowList[0]["DirID"].ToString()),"/");
      }
 
private void CreateChildNode(DropDownList dirList,
DataTable dataTable,int nParentID,string sParentName)
      {           
            ///when selecting data, we use sort expression-"OrderBy"
            DataRow[] rowList =
 dataTable.Select("ParentID='" + nParentID.ToString() +
 "'","CreateDate DESC");
            foreach(DataRow row in rowList)
            {
                  string sName = sParentName + row["Name"].ToString() + "/";
                  ///create a new node
                  dirList.Items.Add(new ListItem(sName,row["DirID"].ToString()));
 
            ///Call this function recursively to create other nodes
                  CreateChildNode(dirList,dataTable,Int32.Parse(row["DirID"].ToString()),sName);
            }
      }

Here, for brevity, we chose to omit all other methods for there are so many of them (see the downloaded source code for detail). Nevertheless, there are some interesting points inside method AddFile. This method will add a new file into the database using a stored procedure called Pr_AddFile which will use parametersName, Parent, Contain, Url and Type that represent file name, the parent directory ID, the size, the linking address, and its type.

Listing 2: Code for function AddFile

      public int AddFile(string sName,int nParentID,int nContain,string sUrl,string sType)
      {
            ///create the connection
            SqlConnection myConnection = new SqlConnection(
                  ConfigurationManager.ConnectionStrings["SQLCONNECTIONSTRING"].ConnectionString);
 
            SqlCommand myCommand = new SqlCommand("Pr_AddFile",myConnection);
            myCommand.CommandType = CommandType.StoredProcedure;
            ///add parameters for the stored procedure
            SqlParameter pName = new SqlParameter("@Name",SqlDbType.VarChar,200);
            pName.Value = sName;
            myCommand.Parameters.Add(pName);          
            SqlParameter pParentID = new SqlParameter("@ParentID",SqlDbType.Int,4);
            pParentID.Value = nParentID;
            myCommand.Parameters.Add(pParentID);            
            SqlParameter pContain = new SqlParameter("@Contain",SqlDbType.Int,4);
            pContain.Value = nContain;
            myCommand.Parameters.Add(pContain);
            SqlParameter pUrl = new SqlParameter("@Url",SqlDbType.VarChar,255);
            pUrl.Value = sUrl;
            myCommand.Parameters.Add(pUrl);
            SqlParameter pType = new SqlParameter("@Type",SqlDbType.VarChar,200);
            pType.Value = sType;
            myCommand.Parameters.Add(pType);
 
            ///define the returned value
            int nResult = -1;
 
            try
            {
                  ///open the connection
                  myConnection.Open();
                  ///execute the SQL clause
                  nResult = myCommand.ExecuteNonQuery();                
            }
            catch(SqlException ex)
            {
                  ///throw exception
                  throw new Exception(ex.Message,ex);
            }
            finally
            {   ///close the connection
                  myConnection.Close();
            }
            ///return nResult
            return nResult;
      }

From above, we can easily see that calling stored procedure Pr_AddFile will result in returning the ID of the new record, which is just what we expect. Here, we see the typical database operation. And since it is a fairly straightforward function and self-explained, we will not examine it in detail any more.

Creating folders

Here comes the page AddFolder.aspx on which we accomplish the task of creating folders. To begin, let us look at its page layout, as shown in Figure 5.

Figure 5: Design-time view of page AddFolder.aspx

Yes, it is a simple but typical interface structure with merely several common controls on the page! Here we also enumerate their functions.

·         DropdownList DirListfor displaying  directory structure info

·         TextBox Namefor user entering the name of the folder

·         RequiredFieldValidator rfNfor checking whether the directory info is empty

·         Button ReturnBtnfor navigating to page Default.aspx

·         Button AddBtnfor creating a new folder

When initialized, page AddFolder.aspx will show directory info in the DropdownList DirList, which is accomplished by calling function BindDirectoryData from inside the event handler Page_Load(object sender, EventArgs e).

Listing 3

    protected void Page_Load(object sender, EventArgs e)
    {
            if(!Page.IsPostBack)
            {   ///display directoy info
                  BindDirectoryData();
            }
    }
      private void BindDirectoryData()
      {   ///display directory info
            Disk disk = new Disk();
            disk.ShowDirectory(DirList,-1);
      }

Clicking the button OK will trigger the event AddBtn_Click(object sender, EventArgs e) which will create a new folder. Inside this event function AddDirecty is called to add the newly-created folder into the underlying database.

When we click the button Return, we are navigated back to page Default.aspx. Listing 12 is the corresponding code for the event ReturnBtn_Click(object sender, EventArgs e).

Listing 4

      protected void AddBtn_Click(object sender,EventArgs e)
      {
            try
            {   ///define a Disk object
                  IDisk disk = new Disk();
                  ///execute the database operation
                  disk.AddDirectory(Name.Text.Trim(),Int32.Parse(DirList.SelectedValue));
                  Response.Write("<script>alert('" + 
"You've succeded in adding the info,safekeep you data!');</script>");
            }
            catch(Exception ex)
            {   ///jump to the exception handling page
                  Response.Redirect("ErrorPage.aspx?ErrorMsg=" +
 ex.Message.Replace("<br>","").Replace("\n","")
                        + "&ErrorUrl=" +
 Request.Url.ToString().Replace("<br>","").Replace("\n",""));
            }
      }

Renaming

In this sample, renaming the file and renaming the folder are achieved in different web pages, EditFolder.aspx (see Figure 6) and EditFile.aspx (see Figure 7).

Figure 6: Design-time view of page EditFolder.aspx

Figure 7: Design-time view of page EditFile.aspx

Seeing the screenshots above, some readers may ask, "Why not put the two together?" The reason is that when we design interface IDisk, we define two independent functions, BindDirectoryData and BindFileData, to gain loose coupling. Since the two parts have nearly the same logic, let us merely concentrate on the code snippet associated with renaming the folder in Listing 11.

Listing 5: the key code in relation to renaming the folder

      int nDirID = 2;
    protected void Page_Load(object sender, EventArgs e)
    {
            ///obtain the value of parameter DirID
            if(Request.Params["DirID"] != null)
            {
                  if(Int32.TryParse(Request.Params["DirID"].ToString(),out nDirID) == false)
                  {
                        return;
                  }
            }
            if(!Page.IsPostBack)
            {   ///display the name of the directory
                  if(nDirID > -1)
                  {
                        BindDirectoryData(nDirID);
                  }
            }
    }
 
      private void BindDirectoryData(int nDirID)
      {
            IDisk disk = new Disk();
            SqlDataReader dr = disk.GetSingleDirectory(nDirID);
            if(dr.Read())
            {
                  Name.Text = dr["Name"].ToString();
            }
            dr.Close();
      }
      protected void EditBtn_Click(object sender,EventArgs e)
      {
            try
            {   ///define the object
                  IDisk disk = new Disk();
                  ///fulfill the database operation
                  disk.EditDirectory(nDirID,Name.Text.Trim());
                  Response.Write("<script>alert('" + 
"Modifying sucessfully,safekeep you data! " + "');</script>");
            }
            catch(Exception ex)
            {   ///jump to exception handling page
                  Response.Redirect("ErrorPage.aspx?ErrorMsg=" + 
ex.Message.Replace("<br>","").Replace("\n","")
                        + "&ErrorUrl=" +
 Request.Url.ToString().Replace("<br>","").Replace("\n",""));
            }
      }

Here, function BindDirectoryData(int nDirID) get info of the specified folder from the database using parameter nDirID and then display the folder's name in the TextBox Name. When we click the button OK, the event EditBtn_Click is triggered to call function EditDirectory to rename the folder and persist the new name in the database. As far as renaming a file is concerned, please see the downloadable source code accompanying this article.

Deleting

Deleting files/folders is also accomplished inside page Default.aspx. To do so, you can just click the button X and then the selected object will be deleted.

When we use Windows Explorer.exe to delete something, a dialog is always appearing to remind us of the possible danger. To also achieve such an effect, we add the related code into the RowDataBound event of the control DiskView (see Listing 6).

Listing 6

      protected void DiskView_RowDataBound(object sender,GridViewRowEventArgs e)
      {
            ImageButton deleteBtn = (ImageButton)e.Row.FindControl("DeleteBtn");
            if(deleteBtn != null)
            {
                  deleteBtn.Attributes.Add("onclick",
"return confirm('Are you sure to delete all the selected items?');");
            }
      }

Here, if the button deleteBtn is not null, then we attach a reminding dialog to it.

The main task to make the deletion is done within the GridView's RowCommand event.

Listing 7

      protected void DiskView_RowCommand(object sender,GridViewCommandEventArgs e)
      {
            if(e.CommandName == "delete")
            {
                  try
                  {   ///delete data
                        IDisk disk = new Disk();
                        disk.DeleteFile(Int32.Parse(e.CommandArgument.ToString()));
 
                        ///rebind the controls' data
                        BindDirectoryData(Int32.Parse(DirList.SelectedValue));
                Response.Write("<script>alert('" + 
"Deleting successfully,safekeep your data!" + "');</script>");
                  }
                  catch(Exception ex)
                  {   ///jump to the page dealing with exception handling
                        Response.Redirect("ErrorPage.aspx?ErrorMsg=" +
 ex.Message.Replace("<br>","").Replace("\n","")
                              + "&ErrorUrl=" + 
Request.Url.ToString().Replace("<br>","").Replace("\n",""));
                  }
            }
      }

Here, event RowCommand obtains the value of the property CommandName of the button X and save it in parameter CommandName. Next, we get the value of the property CommandArgument and convert it into an integer which is stored in the parameter nDirID. If all the requirements are satisfied, then we start to delete the selected object (file or folder) which is fulfilled by calling the function DeleteFile(int nDirID) of interface IDisk, removing the item (whose value of DirID is nDirID) from the database. Finally, an appropriate dialog appears to indicate whether the operation is successful or not.

Moving the files/folders

Moving files or folders is also accomplished through page Default.aspx. When clicking the button MoveTo, you can move the selected files or folders to another path. To do this, we will follow the steps listed below.

1.    Select the files/folders to move.

2.    Select the destination folder.

3.    Click button MoveTo to finish the task.

All the moving functions are fulfilled inside function MoveBtn_Click(object sender, EventArgs e), whose total code is shown below.

Listing 8

      protected void MoveBtn_Click(object sender,EventArgs e)
      {
            try
            {   ///define a Disk object
                  IDisk disk = new Disk();
                  foreach(GridViewRow row in DiskView.Rows)
                  {
                        CheckBox dirCheck = (CheckBox)row.FindControl("DirCheck");
                        if(dirCheck != null)
                        {
                              if(dirCheck.Checked == true)
                              {
                                    ///fulfill database operation
                                    disk.MoveDirectory(Int32.Parse(
DiskView.DataKeys[row.RowIndex].Value.ToString()),
                                          Int32.Parse(MoveDirList.SelectedValue));
                              }
                        }
                  }
                  ///rebind the data of the controls
                  BindDirectoryData(Int32.Parse(DirList.SelectedValue));
                  Response.Write("<script>alert('" +
 "Modifying successfully,safekeep your data!" + "');</script>");
            }
            catch(Exception ex)
            {   ///jump to the exception handling page
                  Response.Redirect("ErrorPage.aspx?ErrorMsg=" +
 ex.Message.Replace("<br>","").Replace("\n","")
                        + "&ErrorUrl=" + 
Request.Url.ToString().Replace("<br>","").Replace("\n",""));
            }
      }

To begin, we obtain the files or folders to move, get the DirID of the destination folder, and finally call the function MoveDirectory(int nDirID, int nParentID) to achieve the movement. Since there is nothing especially unusual going on here and the code is self-explaining, we do not chatter much.

Retrieving attributes

Now we turn our attention to the page ViewDisk.aspx on which we accomplish the task of retrieving file attributes, whose buddy code-behind file is ViewDisk.aspx.cs. As usual, let us first look at its page layout.

Figure 9: Design-time view of page ViewDisk.aspx

Since the controls in Figure 9 are all self-explaining, here we choose to omit detailing them and focus on the page initialization.

Listing 9: Initialization of page ViewDisk.aspx

public partial class ViewDisk : System.Web.UI.Page
{
      int nFileID = -1;
      private int nParentID = -1;
protected void Page_Load(object sender,EventArgs e)
      {
            ///Obtain the value of parameter DirID
            if(Request.Params["DirID"] != null)
            {
                  if(Int32.TryParse(Request.Params["DirID"].ToString(),out nFileID) == false)
                  {
                        return;
                  }
            }
            ///Get the value of parameter ParentID
            if(Request.Params["ParentID"] != null)
            {
                  if(Int32.TryParse(Request.Params["ParentID"].ToString(),out nParentID) == false)
                  {
                        return;
                  }
            }
            if(!Page.IsPostBack)
            {  //Retrieve file attributes from the database using parameter nDirID
                  if(nFileID > -1)
                  {
                        BindFileData(nFileID);
                  }
            }
      }
      private void BindFileData(int nDirID)
      {
            IDisk disk = new Disk();
            SqlDataReader dr = disk.GetSingleFile(nFileID);
            if(dr.Read())
            {   ///Get the file name(including the extension)
                  Name.Text = dr["Name"].ToString();
                  Type.Text = dr["Type"].ToString();
                  Contain.Text = dr["Contain"].ToString() + "B";
                  CreateDate.Text = dr["CreateDate"].ToString();
                  
                  ///Dynamically create the folder under which the file lies
                  Dir.Text = CreateDir(Int32.Parse(dr["DirID"].ToString()));
            }
            dr.Close();
            
      }
public string CreateDir(int nDirID)
      {
            StringBuilder dirSB=new StringBuilder();// create a new string - dirSB
            //Obtain folders from inside the database
            IDisk disk = new Disk();
            DataTable dataTable = SystemTools.ConvertDataReaderToDataTable(disk.GetAllDirectoryFile());   
 
            DataRow[] rowList = dataTable.Select("DirID='" + nDirID.ToString() + "'");
            if(rowList.Length != 1) return("");
            ///Create other folders
            InsertParentDir(dataTable,Int32.Parse(rowList[0]["ParentID"].ToString()),dirSB);
            return (dirSB.ToString());
      }
 
private void InsertParentDir(DataTable dataTable,int nParentID,StringBuilder sDir)
      {
            if(nParentID <= -1)
            {
                  return;
            }
            DataRow[] rowList = dataTable.Select("DirID='" + nParentID.ToString() + "'");
            if(rowList.Length != 1) return;
            //Insert the folder info
            sDir.Insert(0,rowList[0]["Name"].ToString() + "/");
      //Call this function recursively and insert corresponding folder info 
      InsertParentDir(dataTable,Int32.Parse(rowList[0]["ParentID"].ToString()),sDir);
      }
protected void ReturnBtn_Click(object sender,EventArgs e)
      {
            Response.Redirect("~/Default.aspx?ParentID=" + nParentID.ToString());
      }
}

When initialized, page ViewDisk.aspx first obtains parameters nDirID and nParentID from the address bar and then show the retrieved file attributes onto the page, which is achieved by calling function BindFileData(int nDirID). With the help of parameter nDirID, function BindFileData retrieves file attributes from the database and then we use controls Name, Dir, Type, Contains and CreateDate to display the name, hosting folder, type, size, and creating date of the file respectively. There is one thing to notice since we have not directly persisted the folder under which the file lies into the database, we have to dynamically create it. This, obviously seen from above, is finished in functions CreateDir(int nDirID) and InsertParentDir(DataTable dataTable, int nParentID, StringBuilder sDir). The following steps are taken so as to create the folder the file belongs to.

1.    Define a string variable to hold the folder.

2.    Retrieve all the folders from the database and save the results into a DataTable instance.

3.    Get the DirID of the parent directory of the current file and add its name to the string dirSB.

4.    Get the parent directory of the file in Step 3 and iterate over Step 3 until the variable ParentID becomes 1. Here, Steps 1 and 2 are done in function CreateDir(int nDirID), while Steps 3 and 4 are finished within function InsertparentDir.

When we click the button Return, we are navigated back to page Default.aspx (the main page), which is accomplished by only one-line code above.

The last note should be mentioned that, as with the typical ASP.NET applications, in this sample we have also constructed an error handling pageErrorPage.aspx. Whenever there is something wrong, we take the policy to redirect the current page to page ErrorPage.aspx, letting it deal with the error-related information.


View Entire Article

User Comments

Title: Great stuff   
Name: Raymond Besiga
Date: 2009-12-04 8:29:10 AM
Comment:
Hello Zhu,

I must say this is the best thing i have come across this month. I started asp.net development recently and i got bored with just reading.trying out projects such as these is way better.Thanks a lot.In case there are other articles you would recommend please don't hesitate.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2019 ASPAlliance.com  |  Page Processed at 2019-03-24 6:01:26 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search