Mapping web form fields to business layer to database
Very interesting post here: http://blogs.msdn.com/darien/archive/2004/11/21/267478.aspx. It's titled The impedance mismatch between object-oriented constructs and relational databases which I know isn't very inviting, but its author is sufficiently mired in academia to think this is a catchy title so let's not hold that against him.
Given that most web applications just provide HTML interfaces to a database, this topic is extremely relevant to all of us. With a tradition 3- or 4-tier web application (say, the ASP.Net Quickstart Issue Tracker), each layer has its own set of fields as well as mappings between those fields and the fields above and beneath it. This means that if you want to add an "IssueSubtitle" field to the Issue object, you'd have to add mappings for it at each layer:
- "Edit Issue" page's code-behind needs to map the Subtitle textbox to the Subtitle field in the business layer
- Business layer needs to map its Subtitle field to the Data Access layer
- Data Access layer needs to map its Subtitle field to the Subtitle property of the update Stored Procedure
- The update Stored Procedure needs to map its Subttitle field
(I won't even go into the mappings required to retrieve the Subtitle field at the other end)
How anyone can think this is "scalable" architecture, or "the right way of doing things", I can barely imagine. It is impossible to maintain, and it doesn't take advantage of any of the features of .Net that could avoid this painful duplication.
Don't get me wrong - I'm all for business layers. For complicated web applications business layers are the only reasonable way of ensuring that business rules are followed. But there must be a better way than manually mapping fields at each layer.
Now I think the solution to this is a few really simple helper methods I wrote today for an app I've been working on:
public void BindControlToObject(Control control, object bindObject) {
foreach(System.Reflection.PropertyInfo property in bindObject.GetType().GetProperties()) {
HtmlInputControl foundControl = control.FindControl(property.Name) as HtmlInputControl;
if(foundControl != null) {
property.SetValue(bindObject, foundControl.Value, null);
}
}
}
public void BindObjectToControl(Control control, object bindObject) {
foreach(System.Reflection.PropertyInfo property in bindObject.GetType().GetProperties()) {
Control foundControl = control.FindControl(property.Name);
if(foundControl != null) {
if(foundControl is Label) {
((Label)foundControl).Text = property.GetValue(bindObject, null).ToString();
}
}
}
}
This will handle the mapping between form fields and our business layer classes. Now, we can use these two helper methods to handle the mapping between business layer classes and DataRows:
public void BindObjectToDataRow(object bindObject, DataRow dataRow) {
foreach(System.Reflection.PropertyInfo property in bindObject.GetType().GetProperties()) {
if(dataRow.Table.Columns.Contains(property.Name)) {
dataRow[property.Name] = property.GetValue(bindObject, null);
}
}
}
public void BindDataRowToObject(object bindObject, DataRow dataRow) {
foreach(System.Reflection.PropertyInfo property in bindObject.GetType().GetProperties()) {
if(dataRow.Table.Columns.Contains(property.Name)) {
property.SetValue(bindObject, dataRow[property.Name], null);
}
}
}
I don't think it would require much imagination to come up with something that would handle the mapping between business layer classes and stored procedure parameters. Any ideas?
I'm sure there are performance issues for using reflection in this way, but when you think about how emporing these techniques can be for you as a developer, these issues seem a lot less important. They can allow you to focus on the true engineering challenges of web application building, rather than spending hours creating endless mappings between tiers.