WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

Build a Web-Based Bug Tracking App
Pages: 1, 2, 3, 4, 5

The result of this is that if there is a BugID stored in Session ("Edit"), the page is prefilled, as shown in Figure 10.



prefilled report
Figure 10. Prefilled Bug Report

Note that at the top of the page, the current update is for Bug 13 and will be the third revision. It is expected that the user will, at this point, modify whichever fields should be changed.

Design Decision: The presentation shown in Figure 10 only works if you already understand what you are seeing (the previous revision that, once you save your changes, will be revision 3). If this product would be used by more than a few people, the UI design might have to be revisited.

There are some additional interesting decisions to make now, including:

  • Should we restrict which fields can be edited based on the user's role (e.g., only a manager can change the Severity)?
  • Should the user be able to change the short description?

The latter question is interesting. The argument in favor is that the short description provides a quick way to show what is happening with the bug, but the (perhaps more compelling) argument against is that the short description is, effectively, the name of the bug, and having it change every time it is modified is bound to cause confusion.

This is also a place where potential features could creep in, the most notable of which is the ability to add new Statuses if none of the available choices meet your needs. For now, to keep this tiny and simple, we'll make such adjustments directly to the Severities table, but this may well be a feature we'll want pretty quickly.

Once we know that this is a revision, we'll need to save that fact in session state (along with the current BugID and BugHistory ID) so that when the user clicks the Save button, that information will be available to our event handler:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        // see if this is a revision
        DataView dv = 
          this.BugsDataSource.Select(new DataSourceSelectArguments()) as DataView
        if (dv != null)  // yes it is a revision
        {
            DataTable dt = dv.Table;

            // get the row for the latest BugHistory
            DataRow dr = dt.Rows[0];  

            int BugID = Convert.ToInt32(dr["BugID"]);
            lblHeader.Text = "Update Bug " + BugID.ToString();

            int bugHistoryID = Convert.ToInt32(dr["BugHistoryID"]);
            bugHistoryID++;  // set the new BugHistory number
            lblHeader.Text += "    Revision: " + bugHistoryID.ToString();

            this.txtShortDescription.Text = dr["ShortDescription"].ToString();
            this.txtLongDescription.Text = 
                dr["LongDescription"] == null ? 
                    string.Empty : dr["LongDescription"].ToString();

            this.txtNotes.Text = dr["Notes"] == null ? 
                string.Empty : dr["Notes"].ToString();
            this.ddlOwner.SelectedValue = dr["Owner"].ToString();
            this.ddlSeverity.SelectedValue = dr["Severity"].ToString();
            this.ddlStatus.SelectedValue = dr["Status"].ToString();

            // stash away what we'll need to save the update 
            Session["IsUpdate"] = true;
            Session["BugID"] = BugID;
            Session["BugHistoryID"] = bugHistoryID;

        }   // end if dv not null
    }       // end if not postback
}           // end method

The essence of this code is that if this is a revision, we prefill the appropriate fields and save the session data we'll need. We can put this to use in the Save_Click event handler:

protected void btnSave_Click(object sender, EventArgs e)
{
    // get the current user (to use for updated by)
    string currentUser = Page.User.Identity.Name;
    int numInserted = 0;

    // either update or insert. Either inserts a new record
    // in BugHistory but only Insert creates a new bug
    if (Session["IsUpdate"] == null)
    {
        this.BugsDataSource.InsertParameters.Clear();
        this.BugsDataSource.InsertParameters.Add(
            "ModifiedBy", currentUser);
        this.BugsDataSource.InsertParameters.Add(
            "ShortDescription", this.txtShortDescription.Text);
        this.BugsDataSource.InsertParameters.Add(
            "LongDescription", this.txtLongDescription.Text);
        this.BugsDataSource.InsertParameters.Add(
            "Severity", this.ddlSeverity.SelectedValue);
        this.BugsDataSource.InsertParameters.Add(
            "Notes", this.txtNotes.Text);
        this.BugsDataSource.InsertParameters.Add(
            "Status", this.ddlStatus.SelectedValue);
        this.BugsDataSource.InsertParameters.Add(
            "Owner", this.ddlOwner.SelectedItem.Text);
        numInserted = this.BugsDataSource.Insert();
        Session.Remove("IsUpdate");
    }
    else
    {
        this.BugsDataSource.UpdateParameters.Clear();
        this.BugsDataSource.UpdateParameters.Add(
            "BugID", Session["BugID"].ToString());
        this.BugsDataSource.UpdateParameters.Add(
            "BugHistoryID", Session["BugHistoryID"].ToString());
        this.BugsDataSource.UpdateParameters.Add(
            "ModifiedBy", currentUser);
        this.BugsDataSource.UpdateParameters.Add(
            "ShortDescription", this.txtShortDescription.Text);
        this.BugsDataSource.UpdateParameters.Add(
            "LongDescription", this.txtLongDescription.Text);
        this.BugsDataSource.UpdateParameters.Add(
            "Severity", this.ddlSeverity.SelectedValue);
        this.BugsDataSource.UpdateParameters.Add(
            "Notes", this.txtNotes.Text);
        this.BugsDataSource.UpdateParameters.Add(
            "Status", this.ddlStatus.SelectedValue);
        this.BugsDataSource.UpdateParameters.Add(
            "Owner", this.ddlOwner.SelectedItem.Text);
        numInserted = this.BugsDataSource.Update();

    }
    if (numInserted == 0)
    {
        lblHeader.Text = "Unable to update database!";
        lblHeader.BackColor = System.Drawing.Color.Red;
        lblHeader.ForeColor = System.Drawing.Color.Yellow;
    }
    else
    {
        this.txtNotes.Text = string.Empty;
        this.txtShortDescription.Text = string.Empty;
        this.txtLongDescription.Text = string.Empty;
        this.ddlOwner.SelectedIndex = 0;
        this.ddlSeverity.SelectedIndex = 0;
        this.ddlStatus.SelectedIndex = 0;
        lblHeader.Text = "Database updated";
        lblHeader.BackColor = System.Drawing.Color.White;
        lblHeader.ForeColor = System.Drawing.Color.Black;
    }
    Response.Redirect("TBTReview.aspx");
}

For this code to make sense, however, we need to look at the Insert and Update commands in the data source, each of which calls a stored procedure: spNewBug and spUpdateBug, respectively. The code for updating a bug creates a new entry in BugHistories:

Create PROCEDURE [dbo].[spUpdateBug]
@BugID int,
@BugHistoryID int,
@ModifiedBy varchar(100),
@ShortDescription varchar(50),
@LongDescription ntext,
@Severity int,
@Notes ntext,
@Status int,
@Owner varchar(100)
AS
BEGIN
      insert into BugHistories ( BugID, BugHistoryID, ModifiedBy, 
      ShortDescription,LongDescription, Severity, Notes, 
      Status, Owner)
      values (
      @bugID, @BugHistoryID, @ModifiedBy, @ShortDescription,
      @LongDescription, @Severity, @Notes, @Status, @Owner)
END

If we're creating a new bug, however, we need to add an entry in both the Bugs table and the BugHistories table, and to ensure the integrity of the database, we want to do that within a transaction:

Create PROCEDURE [dbo].[spNewBug]
@ModifiedBy varchar(100),
@ShortDescription varchar(50),
@LongDescription ntext,
@Severity int,
@Notes ntext,
@Status int,
@Owner varchar(100)
AS
BEGIN
Begin Transaction 
   declare @bugID as int
   Insert into Bugs (PlaceHolder) values (@ShortDescription)
   select @bugID = @@identity
   if @@error <> 0 goto errorHandler
   insert into BugHistories ( BugID, BugHistoryID, ModifiedBy, 
      ShortDescription,LongDescription, Severity, Notes, 
      Status, Owner)
      values (
      @bugID, 1, @ModifiedBy, @ShortDescription,
      @LongDescription, @Severity, @Notes, @Status, @Owner)
   if @@error <> 0 goto errorHandler
   commit transaction
   goto done
errorHandler:
   rollback transaction
done:
END

Of particular note here is that neither the BugID nor the BugHistoryID is passed to spNewBug. In this stored procedure, the BugID will be generated by the database (BugID is an identity column) and the BugHistoryID will be set to 1. The TimeStamp is also set by defining the field to call GetDate(). Once the Bug has been added, we assign the new bugID (held in the @@identity parameter) to our local variable @BugID, and then use that to create the record in BugHistories. If either Insert command fails, the transaction is rolled back; if all goes well, it is committed. The C# code examines the return value to see if rows were updated; if not, an error is registered.

if (numInserted == 0)
{
    lblHeader.Text = "Unable to update database!";
    lblHeader.BackColor = System.Drawing.Color.Red;
    lblHeader.ForeColor = System.Drawing.Color.Yellow;
}

Pages: 1, 2, 3, 4, 5

Next Pagearrow