Active Record and ASP.Net 2.0
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" |
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,
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.
Comments
Comment preview