Introduction
Most of you would have come across or used a generic framework for object persistence in .NET. I have used two different schemes - one of them decorates the fields/properties with attributes and the framework uses reflection to retrieve metadata and persist the object (See Figure 1). In the other scheme, the metadata is externalized using XML files (See Figure 2) and again reflection is used to complete the persistence.
Pros and cons
I personally prefer the externalized XML scheme for the following reasons:
- Even if the source code is not available, we can define mapping for classes and store them (very useful when third party libraries are used).
- Class definitions are not cluttered with mapping data.
- Multiple mapping can be defined if the same object has to be persisted in different schema. Basically it allows for flexibility and extensibility.
In my personal view, both the schemes are fragile in nature. There is no type safety or compile time check to ensure that the mappings are complete and valid.
Improved solution
I recently developed a prototype framework for object-relational persistence which provides basic data CRUD (Create, Retrieve, Update, Delete) services to business objects using the externalized XML mapping scheme. I ensured that the scheme can embrace System.Transaction
for transaction support.
In Figure 3, you can see the class definition, mapping data and the code snippet which creates an instance of the employee class and saves it in a database. This implementation is like other existing implementations and I have added value to it using a new feature introduced in ASP.NET 2.0 compilation model:
I wrote a custom build provider and mapped it to files with extension ".orm" in the App_code folder (see Figure 4). This custom build provider parses the existing mappings and generates a wrapper class for each class mapping which ensured that only the defined operations are applicable on the business object.
For example, if the ORM file had mapping definition for INSERT
and UPDATE
operations only, then the wrapper class will have methods only for Insert
and Update
operations. If the class name is not spelled correctly or if there are duplicate mapping definitions for the same class, an error is thrown during compilation. If the developer makes changes to the mapping definitions and saves the files, Visual Studio 2005 immediately recognizes the change, initiates the build and intellisense is made available for the newly added methods immediately (See Figure 5):
Figure 6 shows a sample code generated by the build provider behind the scenes. Using this approach, the fragile mapping definitions in the XML files are validated and made to safe code during compilation time. This will greatly reduce the developers time during development and unit testing cycles:
Improvements
This build provider can be further extended to verify whether all the stored procedures in the mapping files exist in the database or not, verify whether all the required stored procedure parameters are mapped or not and more...
Conclusion
I strongly believe that this compilation support will add more value to the existing object relational persistence schemes and open up many more possibilities.