Monday, October 27, 2008

User friendly error handling in asp.net web pages

One of the beauties of an asp.net web application charms from the user friendly error handling. User friendly error handling does not mean that there be no errors but it quite means that users may not be afraid of the user error pops up to the user interface. But still, beauty of error handling does not limit to end users, but also means a lot for web masters. In one of the ways to handling errors in an asp.net web application, I once wrote an appreciable way to handling errors in SQL Server 2005 database level. Today I am describing a tiny step that can improve user experience to the transaction errors.


Fig. Displaying user friendly error messages in asp.net web page

After having displayed user level and application level errors in so many pages, I came across the idea that these should be presented in some attractive way that represent one or most of: clarity, better color combination, eligible at a glance, uniqueness and so on. So these days I use a simple error control that tries to fulfill these requirements. Please don't get puzzled. All I am doing is create a user control consisting table layout and a label, add an Image control and reference to some images that represent success (like right sign) and failure (like cross sign in red) and display appropriate one according to the success or failure in a page operation.

Here is the CustomErrorControl.aspx code

<%@
Control
Language="C#"
AutoEventWireup="true"
CodeFile="ErrorControl.ascx.cs"
Inherits="Controls_ErrorControl" %>
<table
id="tblError"
bordercolor="blue"
width="100%"
height="50px"
runat="server"
cellpadding="1"
cellspacing="1"
bgcolor="#ffffcc"
bordercolordark="#0099ff">
<tr>
<td
style="width: 20px; height: 48px"><asp:Image
ID="imgErrorSuccess"
runat="server"
/></td>

<td
style="width: 580px; height: 48px"><asp:Label
ID="lblError"
runat="server"
Font-Bold="False"
Font-Names="Arial"
Font-Size="11pt"
EnableViewState="false"></asp:Label></td>

</tr>

</table>

Here is the code behind.

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; System.Web.UI.HtmlControls; using System.Drawing;

public
partial
class
Controls_ErrorControl : System.Web.UI.UserControl

protected
void Page_Load(object sender, EventArgs e)
{ }
public
void ShowErrorMessage(bool success, string message)
{
if (success == true)
{

imgErrorSuccess.ImageUrl = "~/Images/success.gif"; lblError.Text = message;

lblError.ForeColor = Color.Green;

}
else
{

imgErrorSuccess.ImageUrl = "~/Images/error.gif";

lblError.Text = message;



lblError.ForeColor = Color.Red;

}

}

}

Now you can reference the user control from any page and use it's method ShowErrorMessage to display success or failure message. This method takes two parameters as inputs. First is success (bool) and another is message to be displayed (string). If [success] is true, the message passed as string will be displayed with corresponding success image, else with error image.
You can customize it to make more functional. For instance for failure I have used red message text and for success I have used green message text displays. Anyway, I just wanted to focus one of several ways to better represent error messages to users.
Fedbacks re always welcome. Happy Programming!

ObjectDataSource 'myObjDataDource' could not find a non-generic method 'myMethod' that has parameters: my_ID

Most programmers working with asp.net ObjectDataSource are bound to be irritated by this error: ObjectDataSource 'myObjDataDource' could not find a non-generic method 'myMethod' that has parameters: my_ID


 

I guess this is due to the nature the asp.net ObjectDataSource does so many things automatically. As I have described in this article that well describes how to do master detail data display using asp.net GridView and DetailsView controls by use of asp.net ObjectDataSource, we can use business objects to perform various database operations using asp.net ObjectDataSource control. All we have to do is define the correspomding methods in the business logic layer (BLL), which can be done easily using either three tier or two tier asp.net web application architecture, either involving only . business logic layer (BLL) or using both business logic layer (BLL) and Data Access Layer (DLL). So there are some points that will help detect why the "objectdatasource could not find the non-generic method" error occurs.

  1. Make sure all methods [SelectMethod, UpdateMethod, DeleteMethod, SelectCountMethod, InsertMethod] used by the ObjectDataSource are typed correctly.
  2. Check to verify the Type actually contains the above methods (mostly programmers may have misspelled them).
  3. Third but important point: do these methods contain exact parameters as pointed in the error, both in number and order. Remember that the order of parameters mean a lot for ObjectDataSource methods. (And perhaps you too are stumbled here! At least I had this problem.)

Follow the above steps and you are nearest to the solution. At least my experience tells these careful steps will guide you understand the core of asp.net objectdatasource control's behavious. Please share if you have similar or advanced experiences regarding this "objectdatasource could not find the non-generic method" error.

Happy programming!

Sunday, October 26, 2008

Windows Forms like Messagebox in asp.net web page

I have often come accross web pages where web programmers resent they miss the MessageBox that is available and highly used in windows programming. And for sure I am no exception. While searching for MessageBox functionality in asp.net web page, I have found one which is quite simple and useful.

Note the MessageBox function in the code snippet below. It takes a string as a parameter and pops it up in a messagebox window using asp.net Label control and javascript. For illustration of it's use, I have added a textbox with ID txtName and a button with ID btnOK. User enters his/her name in the textbox and clicks ok. If the user input contains special characters &, _ or @, the messagebox will be prompted with the message that name can not contain such charaters. If not, user's name will be prompted using same message box.
Fig: Displaying windows form like MessgeBox in asp.net web page

Here goes the MesageBox function in C#, which we can call anytime. It takes the message text (string) as input and displays as in the figure above. The only trick involved is:
1. Create a new label from code behind
2.Initialize the text of the lable to a little javascript code [which is a call to javascript's alert function].
3. Add this label to to the page's control bag.

That's all. Now lets look into the function itself.


//the message box function
private void MessageBox(string msg)
{
Label lbl = new Label();

lbl.Text = "<script language='javascript'>" + Environment.NewLine + "window.alert('" + msg + "')</script>";

Page.Controls.Add(lbl);
}


Now lets go with an example. We will have a form in which there is one textbox and one button. We will message user when undesired special character is detected in the textbox. Lets have the form markup like this [within the form].


<body>

<form id="form1" runat="server">

<div>

<asp:Label ID="Label1" runat="server" Text="Your Name:"></asp:Label>

<asp:TextBox ID="txtName" runat="server"></asp:TextBox>

<asp:Button ID="Button1" runat="server" Text="OK" onclick="Button1_Click" />

</div>

</form>

</body>


Now is the time for the event handler on button click.

//invoked when button btnOK is clicked
protected void Button1_Click(object sender, EventArgs e)
{
string name = txtName.Text;
if (!ValidateString(name))
{
MessageBox("You can not put special characters _, & or @ in your name.");
}
else
{
MessageBox("Your name is" + " " + name);
}
}


This is just another function for test purpose only, that validates the characters in the textbox.


//returns true if the string contains _,& or @
//otherwise returns false
public bool ValidateString(string str)
{
if((str.Contains("_") || (str.Contains("&"))||(str.Contains("@"))))
{
return false;
}
else
return true;
}


This MessageBox function, although a simple one can be extensively used throughout the pages, using MasterPage concept or base page concept or anything else that propagates inheritance. The only limitation is that for this method to work javascript must enabled in the browser (and you know this is the issue in all pages that use javascript). Further, for the function ValidateString, which currently checks only three special characters, you can extend it to include other special charactes also.


Your creative suggestions are always welcome! Happy programming!
kick it on DotNetKicks.com

Wednesday, October 22, 2008

Error Handling in SQL Server 2005 Stored Procedure using Try Catch Block

I am fascinated by the easy and safer way SQL Server 2005 provides for error handling. Traditionally we used @@ERROR for this purpose, along the BEGIN TRANSACTION, COMMIT TRANSACTION, ROLLBACK TRANSACTION blocks. Below is one example.

Traditional Error Handling in SQL Server 2000/2005

ALTER PROCEDURE dbo.DeleteFromMyTable

AS

/* SET NOCOUNT ON */

BEGIN TRANSACTION

DELETE FROM myTable1 WHERE myColumn='myColumnValue'; --delete transaction1

IF @@ERROR>0 --some error occured

BEGIN

ROLLBACK TRANSACTION

RETURN

END

ELSE --no error has occured till now, move ahead (do some more transactions)

BEGIN

BEGIN TRANSACTION

DELETE FROM myTable2 WHERE myColumn='myColumnValue'; --delete transaction2

IF @@ERROR>0 --some error occured at this point

BEGIN

ROLLBACK TRANSACTION
--this rollbacks previous delete action also

RETURN

END

ELSE
--both the delete transactions successful !

BEGIN

--so commit the transactions

COMMIT TRANSACTION

RETURN

END

END

All these stuffs work (perfectly!). But oh! SQL Server 2005! There exists more easy and interesting way. If you are an asp.net programmer or the one who works with front end, there is exactly like what a developer faces with error handling. Guess what? You are right, it is Try..Catch block. Yes Try..Catch block has been introduced in SQL Server 2005, and it provides better way to performing transactions like above in SQL Server database.

Latest Error Handling in SQL Server 2005

ALTER PROCEDURE dbo.DeleteFromMyTable

AS

/* SET NOCOUNT ON */

BEGIN TRY

BEGIN TRANSACTION

DELETE FROM myTable1 WHERE myColumn='myColumnValue'; --delete transaction1

DELETE FROM myTable2 WHERE myColumn='myColumnValue'; --delete transaction2

COMMIT TRANSACTION

END TRY

BEGIN CATCH

IF @@TRANCOUNT > 0 --some error has occurred

ROLLBACK TRAN --so rollback all transactions

--you raise error or fill output variable here

--for front end reference in your asp.net webpage

END CATCH

See how easy and better way? I found this Try..Catch has made sql programming like front end programming. Happy Programming!

Tuesday, October 21, 2008

mailto: Formatted Email Address in asp.net GridView or DetailsView Column using HyperLink control

I had an email address field in a GridView and I implemented a master detail data display using an asp.net GridView and asp.net DetailsView Controls. So I wished to display the email addresses so that one could directly email someone. I maintained to use a hyperlink and give a try to the old method, in which I added one HyperLinkField field in the columns field of an asp.net GridView, like below.

<asp:HyperLinkField
HeaderText="E-mail"
DataTextField="Email"
DataNavigateUrlFormatString="mailto:{0}"
DataNavigateUrlFields="Email"
/>

But this did not work. Using HyperLink only displays the email address but does not provide the link to mail. They say there are some security reasons associated with ImageField and HyperLink Field in the later version. So I had to go much search and finally I found it. The trick is simple. Add a template field and use a hyperlink control, like the one below.

<asp:TemplateField
HeaderText="E-mail"
SortExpression="Email">


<ItemTemplate>


<asp:HyperLink
ID="HyperLink1"
runat=server
Text='<%# Eval("Email") %>'
NavigateUrl='<%# Eval("Email", "mailto:{0}") %>'
/>


</ItemTemplate>


</asp:TemplateField>

Note: You can place your own Email Data column field in the Eval( "myFiledID") expression.

Happy Programming!!

Monday, October 20, 2008

Master Detail Data Display using an asp.net GridView and asp.net DetailsView Controls


ObjectDataSource is one of the multifeatured datacontrol in asp.net. In many ways it is similar to the SqlDataSource. For instance both provides data source that is bindable to data displaying controls like Repeater, GridView, FormsView and DetailsView. Similarly, GridView has properties like SelectCommand, InsertCommand, UpdateCommand and DeleteCommand whereas DetailsView provides SelectMethod, InsertMethod, UpdateMethod and DeleteMethod for select, insert, update and delete operations.


Fig: Showing asp.net GridView and DetailsView master detail data display using ObjectDataSource


But here lies some interesting differences. Those command propeties of GridView can be assigned with direct SQL statements. This means we can directly specify select, insert, update and delete commands. In other side, the method properties of ObjectDataSource specify the corresponding methods in the Data Access Layer (DLL) or Business Layer (BLL). All we have to do is specify the class name (along with the namespace, if the class has) in the Type property of the ObjectDataSource. Remember that this class will contain the select, insert, update and delete methods that are directly used by the ObjectDataSource.


By this time you may be thinking about parameters that have to be passed to those methods. Yes, you can specify select, insert, update and delete parameters in the ObjectDataSouce. But all these stuffs are so easy if your are working with GridView, DetailsView, FormsView or Repeater. I have worked in a project using GridView and DetailsView master detail data display using two different ObjectDataSource, one for asp.net GridView and another for asp.net DetailsView.


Since I have well architected three tier architecture in my asp.net web application, I have used the Business Logic Layer (BLL) methods in the ObjectDataSource. I want to share this experiece with you in this article.


To perform the master detail data display in asp.net, we will use GridView as the master data control and DetailsView as the detail data control. First of all lets define a class Category with the public properties Category_ID, Category_Name, Active (if this item is actively displayed in user interfaces). This very class will contain the necessary methods required by the ObjectDataSources.




Category Business Layer and Data Access Layer classes


Category BLL Class


//redefine the public properties and call to classes of DAL


//I have left this part to you to make it short


Category DAL Class


namespace MyTest.DAL {
public class Category
{
public Category() { }

public Category(int id, string categoryName, bool isActive)
{
this.Category_ID = id;

this.Category_Name = categoryName;

this.Active = isActive;


}


private int _category_id = 0;
public int Category_ID
{
get { return _category_id;
}
set { _category_id = value; }
}

private string _category_Name = "";
public string Category_Name { get { return _category_Name; }
set { _category_Name = value; } }

private bool _active;


public bool Active {
get { return _active; }

private set { _active = value; }
}


//methods that work with Category
public abstract List<CategoryDetails> GetCategories(string sortExpression, int pageIndex, int pageSize);
public abstract CategoryDetails GetCategoryByID(int categoryID);
public abstract int GetCategoryCount();
public abstract bool DeleteCategory(int categoryID);
public abstract int InsertCategory(CategoryDetails category);
public abstract bool UpdateCategory(CategoryDetails category);
}
}


GridView, DetailsView and ObjectDataSources in asp.net page (ManageCategory.aspx)


<asp:GridView
ID="gvCategories"
runat="server"
AutoGenerateColumns="False"
DataSourceID="objAllCategories"
Width="80%"
DataKeyNames="Category_ID"
OnRowDeleted="gvCategories_RowDeleted"
OnRowCreated="gvCategories_RowCreated"
OnSelectedIndexChanged="gvCategories_SelectedIndexChanged"
AllowSorting="True"
AllowPaging="True"
PageSize="10">

<Columns>

<asp:BoundField
DataField="Category_Name"
HeaderText="Category"
SortExpression="Category_Name"
/>
<asp:CheckBoxField
DataField="Active"
HeaderText="Is Active"
>
<ControlStyle
Width="5px" />

<ItemStyle
Width="20px"
/>

</asp:CheckBoxField>

<asp:CommandField
ButtonType="Image"
SelectImageUrl="~/Images/Edit.gif"
SelectText="Edit category"
ShowSelectButton="True">
<ItemStyle
HorizontalAlign="Center"
Width="20px"
/>

</asp:CommandField>

<asp:CommandField
ButtonType="Image"
DeleteImageUrl="~/Images/Delete.gif"
DeleteText="Delete category"
ShowDeleteButton="True">
<ItemStyle
HorizontalAlign="Center"
Width="20px"
/>

</asp:CommandField>

</Columns>

<EmptyDataTemplate><b>No categories to show</b></EmptyDataTemplate>
</asp:GridView>
<asp:ObjectDataSource
ID="objAllCategories"
runat="server"
SelectMethod="GetCategories"

TypeName="MyTest.BLL.Category"
DeleteMethod="DeleteCategory"
SortParameterName="sortExpression"
EnablePaging="true"
SelectCountMethod="GetCategoryCount">

</asp:ObjectDataSource>
<p></p>
<asp:DetailsView
Width="80%"
ID="dvwCategory"
runat="server"
AutoGenerateRows="False"
DataSourceID="objCurrCategory"
Height="50px"
AutoGenerateEditButton="True"
AutoGenerateInsertButton="True"
HeaderText="Product Category Details"
OnItemInserted="dvwCategory_ItemInserted"
OnItemUpdated="dvwCategory_ItemUpdated"
OnItemCreated="dvwCategory_ItemCreated"
DefaultMode="Insert"
OnItemCommand="dvwCategory_ItemCommand"
DataKeyNames="Category_ID">
<FieldHeaderStyle
Width="40%"/>
<Fields>
<asp:BoundField
DataField="Category_ID"
HeaderText="ID"
InsertVisible="false"
ReadOnly="true"/>

<asp:TemplateField
HeaderText="Category">

<ItemTemplate>

<asp:Label
ID="lblCategoryName"
runat="server"
Text='<%# Eval("Category_Name") %>'></asp:Label>


</ItemTemplate>
<EditItemTemplate>

<asp:TextBox
ID="txtCategoryName"
runat="server"
Text='<%# Bind("Category_Name") %>'
MaxLength="256"
Width="98%"></asp:TextBox>

<asp:RequiredFieldValidator
ID="valRequireTitle"
runat="server"
ControlToValidate="txtCategoryName"
SetFocusOnError="true"

Text="The Category field is required."
ToolTip="The Category field is required."
Display="Dynamic"></asp:RequiredFieldValidator>


lt;/EditItemTemplate>
</asp:TemplateField>

<asp:CheckBoxField
DataField="Active"
HeaderText="Active"/>

</Fields>

</asp:DetailsView>

<asp:ObjectDataSource
ID="objCurrCategory"
runat="server"
InsertMethod="InsertCategory"
SelectMethod="GetCategoryByID"
UpdateMethod="UpdateCategory"
TypeName="MyTest.BLL.Category">

<SelectParameters>

<asp:ControlParameter
ControlID="gvCategories"
Name="Category_ID"
PropertyName="SelectedValue"

Type="Int32"
/>
</SelectParameters>
</asp:ObjectDataSource>


Presentation Layer Backend (ManageCategory.aspx.cs)


(only required events have been listed)


protected void gvCategories_SelectedIndexChanged(object sender, EventArgs e) { dvwCategory.ChangeMode(DetailsViewMode.Edit); }


protected void gvCategories_RowDeleted(object sender, GridViewDeletedEventArgs e)
{ gvCategories.SelectedIndex = -1; gvCategories.DataBind(); dvwCategory.ChangeMode(DetailsViewMode.Insert);


}


protected void gvCategories_RowCreated(object sender, GridViewRowEventArgs e) {
if (e.Row.RowType == DataControlRowType.DataRow)
{
ImageButton btn = e.Row.Cells[4].Controls[0] as ImageButton;
btn.OnClientClick = "if (confirm('Are you sure you want to delete this category?') == false) return false;";
}
}


protected void dvwCategory_ItemInserted(object sender, DetailsViewInsertedEventArgs e{
gvCategories.SelectedIndex = -1;
gvCategories.DataBind();
}


protected void dvwCategory_ItemUpdated(object sender, DetailsViewUpdatedEventArgs e) {
gvCategories.SelectedIndex = -1;
gvCategories.DataBind();
}


protected void dvwCategory_ItemCreated(object sender, EventArgs e){ //enforce logic to the items created
//before it can be stored to database
//or used anywhere.
}


protected void dvwCategory_ItemCommand(object sender, DetailsViewCommandEventArgs e) {
if (e.CommandName == "Cancel")
{
gvCategories.SelectedIndex = -1;
gvCategories.DataBind();
}
}


By this time, you have displayed the master detail data using asp.net GridView control and asp.net DetailsView control with the use of ObjectDatasource data control. More interestingly, we have also observed architected a three tier asp.net application structure.


Happy programming!

Popular Posts

Recent Articles