Active Record and ASP.Net 2.0

time to read 14 min | 2704 words

This post is prompt by Scott Guthrie’s post about ASP.Net + DLinq and Frans Bouma’s post about LLBLGen Pro v2.0 with ASP.NET 2.0. For completion sake, I think that I should pipe in with how you would do this with Active Record. I strongly urge you to read both posts, since I’m going to go short on text and heavy on code.

 

One thing to note before we begin, both DLinq & LLBLGen Pro posts used the code generation utilities for the demo. While Active Record has a tool that can generate code from existing databases, it is targeted to version 1.1, and is not actively maintained at the moment. So, we will write the code from scratch, and we will enjoy doing it J.  As you will see, there isn’t much code to write.

 

I wrote extensively about Active Record in 2.0 in a previous set of posts.

 

Step 1 : Setting up the project

Create a Web Site or a Web Application project and add a reference to Castle.ActiveRecord.dll

 

Step 2: Create The Customer Class

This is a very simple process, I’m showing only some of the code here, but you can see that it is a matter of simply having a property marked with [Property] per each column in the table.

 

[ActiveRecord("Customers")]

public class Customer : ActiveRecordBase<Customer>

{

 

    [PrimaryKey(PrimaryKeyType.Assigned)]

    public string CustomerID

    {

        get { return customerID; }

        set { customerID = value; }

    }

 

    [Property]

    public string City

    {

        get { return city; }

        set { city = value; }

    }

 

    [Property]

    public string Region

    {

        get { return region; }

        set { region = value; }

    }

 

    [Property]

    public string CompanyName

    {

        get { return companyName; }

        set { companyName = value; }

    }

}

 

Step 3: Setup configuration

 

Create the following web.config file:

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="activerecord"

               type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" />

  </configSections>

      

  <connectionStrings>

    <add name="NorthWind" connectionString="Data Source=localhost;Initial Catalog=Northwind;Integrated Security=True;"/>

  </connectionStrings>

 

  <activerecord isWeb="true">

    <config>

      <add key="hibernate.connection.driver class" value="NHibernate.Driver.SqlClientDriver" />

      <add key="hibernate.dialect"                 value="NHibernate.Dialect.MsSql2000Dialect" />

      <add key="hibernate.connection.provider"     value="NHibernate.Connection.DriverConnectionProvider" />

      <add key="hibernate.connection.connection string" value="ConnectionString = ${NorthWind}" />

    </config>

  </activerecord>

 

</configuration>

 

Create a global.asax file and add the following to the Application Start event handler, which will initialize Active Record:

 

void Application Start(object sender, EventArgs e)

{

    Castle.ActiveRecord.Framework.IConfigurationSource source =

               System.Configuration.ConfigurationManager.GetSection("activerecord") as Castle.ActiveRecord.Framework.IConfigurationSource;

    Castle.ActiveRecord.ActiveRecordStarter.Initialize(typeof(Customer).Assembly, source);

}

 

Step 4: Write a page that uses Active Record

 

Now we can write the page, like this:

 

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits=" Default" %>

 

<html>

<body>

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

        <h1>

            Northwind Customers</h1>

        <asp:GridView ID="GridView1" AutoGenerateColumns="false" runat="server">

            <Columns>

                <asp:BoundField HeaderText="Customer ID" DataField="CustomerID" />

                <asp:BoundField HeaderText="Name" DataField="CompanyName" />

                <asp:BoundField HeaderText="City" DataField="City" />

                <asp:BoundField HeaderText="State" DataField="Region" />

            </Columns>

        </asp:GridView>

    </form>

</body>

</html>

 

And the code behind is simply:

protected void Page Load(object sender, EventArgs e)

{

    GridView1.DataSource = Customer. FindAllByProperty("Country","USA");

    GridView1.DataBind();

}

 

The result is…

Step 5: Data Shaping and Projections

Here Scott took advantage of anonymous types in C# 3.0, which we can’t take advantage of in C# 2.0. Because of this, we need a real type that we will use for projection. It is a very simple one:

We need to add those two columns to the grid:

<asp:BoundField HeaderText="Num Orders" DataField="NumberOfOrders" />

<asp:BoundField HeaderText="Last Order" DataField="LastOrderDate"
              DataFormatString="{0:d}" HtmlEncode="false" />

 

Then, we use a projection query to get the results from the database. What we end up with is this:

protected void Page Load(object sender, EventArgs e)

{

    IActiveRecordQuery query = new HqlBasedQuery(typeof (Customer),

        @"select new CustomerProjection(c.CustomerID, c.CompanyName, c.City,
                     c.Region, count(o), max(o.OrderDate) )

        from Customer c join c.Orders o where c.Country = 'USA'

        group by c.CustomerID, c.CompanyName, c.City, c.Region

        ");

    GridView1.DataSource = ActiveRecordMediator.ExecuteQuery(query);

    GridView1.DataBind();

}

 

Running this code will result in this page:

By the way, a note to all the efficiency freaks out there, the above query result in a  single query sent to the server.

 

Step 6: Hierarchical Binding

Now that we have projections, let us see how we can use Active Record for hierarchical data. Scott’s example used anonymous types and Linq to do some very nice things with hierarchical binding. Mixing aggregation and hierarchies is more of a problem in C# 2.0 and NHibernate. (Solvable, but not within the constraints of demo code)

What is very easy, though, is to work with hierarchies:

 

GridView1.DataSource = Customer.FindAllByProperty("Country", "USA");

GridView1.DataBind();

 

The gird looks like this:

 

<asp:GridView ID="GridView1" AutoGenerateColumns="false" runat="server">

    <Columns>

        <asp:BoundField HeaderText="Customer ID" DataField="CustomerID" />

       <asp:BoundField HeaderText="Name" DataField="CompanyName" />

        <asp:BoundField HeaderText="City" DataField="City" />

        <asp:BoundField HeaderText="State" DataField="Region" />

        <asp:TemplateField HeaderText="Recent Orders">

            <ItemTemplate>

                <ul>

                    <asp:Repeater ID="Repeater1" DataSource='<%# Eval("Orders") %>'

                                                       runat="server">

                        <ItemTemplate>

                            <li>

                                <%# Eval("OrderID") %> -

                                (<%# Eval("OrderDate", "{0:d}")%>) </li>

                        </ItemTemplate>

                    </asp:Repeater>

                </ul>

            </ItemTemplate>

        </asp:TemplateField>

    </Columns>

</asp:GridView>

 

And the result is:

 

 

Step 7: Paging

 

I enabled paging for the grid, set the page size to 3 and that was about it.

I need to handle the OnPageIndexChanging event, of course, but this is trivial.

This is the end result:

 

For completion sake, here is the full code behind:

public partial class Default : System.Web.UI.Page

{

    protected void Page Load(object sender, EventArgs e)

    {

        if(!IsPostBack)

            BindData();

    }

 

    private void BindData()

    {

        GridView1.DataSource = Customer.FindAllByProperty("Country", "USA");

        GridView1.DataBind();

    }

 

    protected void GridView1 PageIndexChanging(object sender, GridViewPageEventArgs e)

    {

        GridView1.PageIndex = e.NewPageIndex;

        BindData();

    }

}

 

Summary:

Well, you have seen how we can do some fairly complex stuff with a minimum of fuss. Enabling complete data binding approach is extremely easy as well, using Active Record inherited static methods and Object Data Source. I may try to put together a web cast showing how it can be done.