I'm not big on ASP.NET, but I've been working on an ASP.NET 2.0 project lately and much to my surprised I faced an issue when combining inheritance and the GridView control.
The GridView control, which was introduced in ASP.NET 2.0, is the preferred grid control as it offers various improvements over its predecessor the DataGrid. One of the improvements is allowing for easier data binding with less code and mark-up.
My requirements were quite simple, load a list of business entities from the database and display them on the page in a tabular format. I decided to use a generic list to hold the business entities and render them on the page using the GridView control. Well that didn't work. The data binding of the GridView worked correctly, but during the rendering of the control an exception would be thrown complaining that a property of my business entity that was bound to a column in the GridView was missing. To be precise the error message is "A field or property with the name 'XXXX' was not found on the selected data source". I doubled checked to make sure I didn't have any spelling mistakes anywhere and that I was indeed using properties that existed in my business entities.
I was confused at first, but then I noticed that it was not the first column being rendered that was failing. I started thinking what was different between the first columns that were rendered successfully and the one that was failing and I realised that the issue was inheritance. All of my business entities inherited from a base class that contained certain properties (e.g. ID). The columns I was trying to render were bound to a mixture of properties, some defined in the base class and others defined in the child class.
The following example illustrates this clearly.
My business entities
/// <summary>
/// Base class for all business entities.
/// </summary>
public abstract class BusinessEntityBase
{
/// <summary>
/// The ID of the business entity
/// that is the primary key in the DB.
/// </summary>
public int ID;
}
/// <summary>
/// A business entity representing a customer.
/// </summary>
public class Customer : BusinessEntityBase
{
/// <summary>
/// The customer's name.
/// </summary>
public string Name;
}
My data binding code
protected void Page_Load(object sender, EventArgs e)
{
// Create a list of customers
List<Customer> customers = new List<Customer>();
Customer customerA = new Customer();
customerA.Name = "CustomerA";
customers.Add(customerA);
Customer customerB = new Customer();
customerB.Name = "CustomerB";
customers.Add(customerB);
// Bind the list of customers to the gridview
this.GridView1.DataSource = customers;
this.GridView1.DataBind();
}
My mark-up for the GridView control
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="ID" HeaderText="Customer ID" />
<asp:BoundField DataField="Name" HeaderText="Customer Name" />
</Columns>
</asp:GridView>
During rendering the following exception is thrown
So how do you work around this issue? I tried searching online, but I didn't come up with anything. I will admit that I didn't do a very thorough investigation, but I just needed to get on with the work so I ditched "BoundField" and went with "TemplateField" instead. By changing the mark-up for the GridView to the following the issue was resolved and the control rendered successfully.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Customer ID">
<ItemTemplate>
<%# ((GridViewTest.Customer)Container.DataItem).ID%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Customer Name">
<ItemTemplate>
<%# ((GridViewTest.Customer)Container.DataItem).Name%>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
I'd still like to know why this issue occurs ...