17 November 2009

Business and Data Layers

What follows in this entry is a way that works well for me to architect a Business Logic Layer (BLL) and Data Access Layer (DAL) into my projects. This may not be the best way, so any constructive feedback is welcome. I love to learn new and more efficient ways of doing things. (Read: I'm lazy, and if I can make it easier on myself, I will.)

Naming

DAL

I tend to like to organize my DAL in a structure similar to the database itself. The namespace ends up being something like:

com.project.DAL.DbName.TableName.Entity.Select.All()

com.project.DAL is the namespace for the DAL project
DbName.TableName is a folder structure
Entity is a class
Select is a nested class
All is a static method
(compare the latter two parts to doing a static method called SelectAll under the Entity class)

If there are specialized procedures that don't map to specific tables, I might make a "Procedures" class under the database folder to encapsulate those.

BLL

The BLL class, I tend to name based on functionality rather than database organization. Something like:

com.project.BLL.CRM.Customer.Get.All()

Interfaces

DAL

The DAL only returns DataTables or DataSets. I don't even bother with typed data sets because of the added hassle of regenerating the XML files each time I make a change.

Returning DataTables has a number of advantages.

For one thing, it's an abstraction over the database. I could even take DataTables pulled from completely different servers, put them in the same DataSet, and use DataRelations to query them in memory to get exactly what I need.

For another thing, if no extra business logic is needed (perhaps the table is only for display, not for editing), then I can skip creation of C# objects at the business layer and bind the DataTable from the DAL directly to web controls. Out of the box, DataTables already support all the niceties of web controls like paging and sorting.

BLL

The business layer objects are Plain Old CLR (C# or VB .NET) Objects (POCOs). They look something like this:
    public class Customer : IInitable  // my interface to make an Init method
{
public int customer_id { get; set; }
public string name { get; set; }
public CustomerTypeEnum type { get; set; }
public DateTime since { get; set; }
public List<History> HistoryEntries { get; private set; }

// note absence of constructor... CLR makes a default constructor this way

#region IInitable Members
public void Init()
{
... // set the HistoryEntries with something like this.HistoryEntries = History.Get.ByCustId(this.customer_id);
}
#endregion

public static class Get // nested class to handle fetch operations
{
public static List<Customer> All()
{
DataTable customer_table = DAL.DbName.Customers.Select.All();
List<Customer> ret_val = Converter.Fill<Customer>(customer_table);
return ret_val;
}
}
}
By convention, I use the same field names from the database as my object property names. That makes converting the data from the database to a C# object easy with reflection. I use a method similar to what I blogged about a while back that does this for me. Something like this:
    public static void Fill(object obj, DataRow row)
{
// get object's writable properties into dictionary by name
...
// loop through each column of the datarow
...
// if the prop name is the same as the column name
// check/handle: null values, enum types, type conversion
// assign obj to property
}
When I need to convert a whole DataTable into a list of objects, I wrote another convenience method:
    public static List<T> Fill(DataTable Table) where T: IInitable, new()
{ // where part means that T implements IInitable interface as has a default constructor
List<T> ret_val = new List<T>();
foreach (DataRow row in Table.Rows)
{
T obj = new T();
Fill(obj, row);
obj.Init(); // IInitable defines this method in case the object needs initialization after filling.
ret_val.Add(obj);
}
return ret_val;
}
IInitable is just a one-method interface that I wrote that makes the C# BLL class implement an Init method that returns void. Because I have to use empty constructors for uniformity, this Init method allows me to still initialize parts that would normally be initialized in the constructor, but after the data is filled in.

The CUD part of CRUD operations, at the BLL layer can take in an object and then use that object's properties for parameters of the DAL method.

However, to be really lazy, I am considering making an overload method for my DAL operations so that the BLL can just pass in the object directly and the overload method will use reflection to pull the properties out of the object and pass them into the method with typed parameters. I can probably encapsulate this with a separate static converter method so that the overload will essentially be a one-line call to the converter method (and therefore not much coding overhead), which invokes the real method. Once I get around to trying this, I'll post something about it.

Not that into NHibernate

I heard great things about Java's Hibernate, so I checked out NHibernate (Hibernate for .NET). After researching it a bit, I believe it will take more time and hassle to maintain than the current way I map data to objects. Here are my reasons.

Maintenance - 3 Sets of things to maintain
  1. C# Objects
  2. XML Mappings (something extra to learn)
  3. Database relations
Maintaining the XML doesn't have to be tedious, since there are tools that can generate it for you from the database schema. But most of the projects I work on are constantly evolving, and data changes are frequent. Assuming I remember to do it, that's a lot of regenerating XML (the same reason I stayed away from typed datasets) in addition to changing C# objects and database structures.

There are also things like NHibernate Mapping Attributes and ActiveRecord. These allow you to forego XML at first (although they ultimately recommend making XML files). But instead of learning XML, you have to learn the appropriate Attribute tags (although admittedly much less pain than the XML, especially ActiveRecord). Fluent NHibernate actually seems like the least pain, but you have to learn and use Fluent's coding conventions (which don't seem out of line) in order to get it to work properly.

Database Independence

This is probably the thing that NHibernate has going for it most. If you use the query language and the criteria language to get all your results, then you really could be connecting to just about any database.

The main issue I have with this is that NHibernate queries != stored procedure calls. For small projects that you don't expect to grow, stored procedures could be an overkill (but so could NHibernate!), but for projects of any complexity store procedures are the way to go. Stored procedure calls have a couple of major advantages, not the least of which being the object-oriented principle of encapsulation (or information hiding if you prefer). If you have a DBA, they can completely rewrite a procedure (for efficiency, say) and your program never knows the difference, which is a Good Thing. The other major advantage is performance as database procedures can be pseudo-compiled, depending on db platform, to make them run faster.

You can use NHibernate to call stored procedures, but the XML just specifies the native stored procedure call syntax ("exec proc_name" in MS SQL and "call proc_name()" in MySQL, for instance) -- there's no real database abstraction advantage there. Worse, if you need to run a procedure that doesn't return a result set, then you just have to make a native query call.

Conclusions

While I had high hopes for using NHibernate to make data access easier, it doesn't seem to accomplish that goal for me. It's an extra thing to learn and maintain, and has a number of drawbacks. Next article (who knows when that will be), I will show the way I architect data access. It might be a laughable thing to some of you (as if anyone reads my blog), but it's the easiest way I've found so far, while still having a lot of flexibility.

10 November 2009

ObjectDataSource TypeName and Nested Classes

When trying to use an ObjectDataSource with an method from my data access layer, I kept getting this error:



The type specified in the TypeName property of ObjectDataSource 'myODS' could not be found.



Long story short, the object I was using was actually a nested class. Even though you write you code like this:

MyNamespace.MyClass.NestedClass.Action();

The actual fully qualified type name for an ObjectDataSource TypeName will be "MyNamespace.MyClass+NestedClass" with a SelectMethod of "Action". If you visit this MSDN page and look down to where it talks about delimiters, you will see that nested classes use a plus as a delimiter for some reason instead of the usual dot.