Monday, March 26, 2012

Object reference not set to an instance of an object

I am trying to write a simple user control to hold a cached HTML "Select" Box

This works fine if I drag the control onto the page directly (in which case I can't work out how to access the control's properties) but errors if I try and programmatically load it into a placeholder

A cut down version of how I am trying to do this is below...

using ShoppingCart_Net.Controls;

namespace ShoppingCart_Net.productdb
{
public class browser_home : System.Web.UI.Page
{
protected CachedListBox clbTypes = new CachedListBox();

private void Page_Load(object sender, System.EventArgs e)
{
string ConnectionString = ConfigurationSettings.AppSettings["ConnectionString"].ToString();
phTypes.Controls.Add(clbTypes);
clbTypes.Query = "Select ProductTypes.ProductTypeID,ProductTypes.ProductTypeDescription From ProductTypes Order By ProductTypes.ProductTypeDescription";
clbTypes.TextField = "ProductTypeDescription";
clbTypes.ValueField = "ProductTypeID";
clbTypes.ConnectionString = ConnectionString;
}

}
}


The CachedListBox.ascx page just contains one line (apart from the directives) as below.

<asp:DropDownList id="lbCached" runat="server"></asp:DropDownList>

The (cut down) code behind for the user control is...

namespace ShoppingCart_Net.Controls
{
using System.Data.OleDb;

public class CachedListBox : System.Web.UI.UserControl
{
protected System.Web.UI.WebControls.DropDownList lbCached;
private string mQuery, mConnectionString, mTextField, mValueField;

private void Page_Load(object sender, System.EventArgs e)
{
if (! this.IsPostBack)
{
OleDbConnection objConn = new OleDbConnection(mConnectionString);
OleDbCommand objComm = new OleDbCommand(mQuery, objConn);
objConn.Open();
try
{
lbCached.DataTextField = mTextField;
lbCached.DataValueField = mValueField;
lbCached.DataSource = objComm.ExecuteReader();
lbCached.DataBind();
}
finally
{
objConn.Close();
}
}
}


public string Query
{
get
{
return mQuery;
}
set
{
mQuery = value;
}
}


//Other Properties omitted for conciseness
}
}

The Error occurs at the first use of lbCached in the Page_Load event above and is as follows...

"Exception Details: System.NullReferenceException: Object reference not set to an instance of an object."

Can anyone see what I am doing wrong?

Cheers!

MartinWell the main problem is that, programmatically you have to load usercontrols(using Page.LoadControl), you can't just create a new instance. Cached list box is a cool idea, but why not really have a listbox Cached? The way you have it is good, but it's really only "caching" your listbox in ViewState, you get that for free anyway. The next user that hits that page would need to go get the data again. My general rule of thumb is if the html output is very simple, say a single control or two, I use custom server controls; if the html output is more complex, like tables, containing serveral controls, or a complex layout, i use user controls(just my .02). Check this out:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CSharpWeb.Components
{
public class CachedListBox : DropDownList
{
private string _query = "";
private string _connString = "";
private string _textField = "";
private string _valueField = "";
public string Query
{
get
{
return _query;
}
set
{
_query = value;
}
}
public string ConnString
{
get
{
return _connString;
}
set
{
_connString = value;
}
}
public string TextField
{
get
{
return _textField;
}
set
{
_textField = value;
}
}
public string ValueField
{
get
{
return _valueField;
}
set
{
_valueField = value;
}
}
public override void DataBind()
{
base.DataSource = getData();
base.DataTextField = this.TextField;
base.DataValueField = this.ValueField;
base.DataBind();
}
private DataSet getData()
{
DataSet ds = null;
if (HttpContext.Current.Cache[this.ID] != null)
ds = (DataSet)HttpContext.Current.Cache[this.ID];
else
{
using(SqlConnection cn = new SqlConnection(this.ConnString))
{
using (SqlCommand cmd = new SqlCommand(this.Query, cn))
{
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
ds = new DataSet();
da.Fill(ds);
HttpContext.Current.Cache[this.ID] = ds;
}
}
}
}
return ds;
}
public void Refresh()
{
HttpContext.Current.Cache.Remove(this.ID);
this.DataBind();
}
}
}
This basically caches the data needed for the control for any user who hits that page. It's saving it under the key name defined by it's ID, so any other controls named the same but on different pages would screw up the cache, just need some unique naming convention.
Then the aspx page would look something like this(fyi, my web project is named CSharpWeb, which will be the assembly reference in the following):

<%@. Page language="c#" Codebehind="CachedListBoxTest.aspx.cs" AutoEventWireup="false" Inherits="CSharpWeb.Components.CachedListBoxTest" %>
<%@. Register TagPrefix="PVB" Assembly="CSharpWeb" Namespace="CSharpWeb.Components" %>
<html>
<body>
<form runat="server">
<PVB:CachedListBox ID="myCachedListBox" Runat="server"/>
<asp:Button ID="btnSubmit" Runat="server" Text="Submit" OnClick="btnSubmit_Click"/>
</form>
</body>
</html>

and the code behind like this:

namespace CSharpWeb.Components
{
public class CachedListBoxTest : System.Web.UI.Page
{
protected CachedListBox myCachedListBox;
protected Button btnSubmit;
protected void btnSubmit_Click(object sender, EventArgs e)
{
//do nothing
}
protected override void OnInit(EventArgs e)
{
if (!Page.IsPostBack)
{

myCachedListBox.ConnString = "user id=sa;password=sa;server=DeathAngel;database=scratch;";
myCachedListBox.TextField = "StateName";
myCachedListBox.ValueField = "StateCode";
myCachedListBox.Query = "Select StateName, StateCode From State";
myCachedListBox.DataBind();
}
base.OnInit (e);
}

}
}
And if the control would be used in multiple places, i'll create a specialized control(in this case a StateDropDownList) that has the data methods built into the control. That way if you're gonna reuse the code you don't have to keep setting the query, connectionstring, textfield, etc. - you'd just create the control and voila, it's a select box with a list of states in it. Anyway, good luck.
Thanks pvb, I misunderstood user control caching - it is a lot clearer now!

cheers for the code - very cool! :cool:

0 comments:

Post a Comment