Mar 4 2009

Nvigorate Design Overview - DatabaseRepository

Category: AlexRobson @ 19:06

On March 5, 2009 at 12:52 AM CST Nvigorate successfully performed its first successful hierarchical fetch & bind of a (simple) domain model. The implications of that statement aren't quite obvious unless you do a lot of work on ORMs so I'll elaborate by giving a high-level design overview of what DatabaseRepository is, what it does and discuss how. I'll save API and more technically detailed posts for later.

Let's start with some of the goals we had in mind for the new version of Nvigorate. As before, one of my top priorities in creating a framework was to make it low impact. What I mean by that is; a lot of frameworks (ORMs specifically) force you to design your classes a certain way, often times forcing you to inherit from MarshalByRef or other odd .Net classes. I've even seen several that had requirements so strict that it dictated the access-level of your properties and fields. Currently, I'd say the most significant requirement Nvigorate carries is that you have to install PostSharp in your dev environment and your build servers. I won't get into the why in this post, but let me say that PostSharp is a very good thing. Gael Fraiteur is a genius. Outside of that, there are no inheritance requirements unless you're going to send your model(s) across WCF. Nvigorate does provide a special base class which you'll have to inherit from in the event that your entities need to cross WCF, I just couldn't figure out a way around it. More on that in a different post. (man, I'm promising a LOT of posts)

Let's get back to the ORM related goals: First off, we wanted a way to perform CRUD operations for light-weight domain models. Second, we wanted more granular control over how the load happened not only for the hierarchy but also for each entity. Third, we wanted it to be so very slick that it completely abstracted the database away from the consuming code.

Binding Engine

This has a few implications for what Nvigorate would have to be able to do. First off, it would need a more robust binding engine. The binding engine currently builds an expression tree and compiles it to a lambda on the fly and the lambda handles the actual binding of data to the domain model. The engine itself, in this go-round, is pretty dumb. All the control of what will get bound and how actually takes place during the query creation. This means simpler binding engine code (I believe it's around 260 lines of code) in terms of volume and, believe it or not, complexity. Granted, expression trees aren't on everyone's "Oh sure, I know that" list, but with Nvigorate, you don't have to know it to get the benefits. I'll have a post about performance tests and benchmarks once I feel I have a complex enough use case for the new engine but believe me; it's very fast and efficient.

The majority of the work in all these CRUD operations is spread across three concerns: mapping, a pseudo-query language and the data access layer.

Maps

Mapping is handled through a central MapIndex which allows multiple mappings to exist for the same entity depending on the data source. The MapIndex handles loading maps automatically from multiple different sources as identified by configuration. This means that MapIndex is never actually called by your client code, you simply configure a block to tell it where to get the maps. Maps can be defined as XML or in attributes but there's a good chance that I'll try to implement fluent maps (like NHibernate now has).

The maps themselves define how multiple sources (in database context, tables) map to a single entity. They also identify how a type relates to other types in the domain (if at all). This information is used during query translation for the database and after a result set is obtained in order to create relationships within the dataset.

Query API

In a previous post, I demonstrated what this API looks like. While the API is exposed for extensibility and flexibility, DatabaseRepository is the intended consumer of the API. It allows the DatabaseRepository to build database agnostic queries which are later translated as part of the DAL stack in Nvigorate. The primary exception to this are the SearchCriteria, CriteriaClause and Criterion classes. These classes provide a way for consumers to easily filter their results using where-clause-like syntax. Because there's a translation layer involved, the end user can build the criteria against the properties of their entity rather than the columns of the underlying database. This is just one of many ways that Nvigorate insulates your code against database changes.

Database Access Layer

Nvigorate's data access layer is a robust, data-agnostic API which handles / encapsulates ADO.Net functionality. For the ORM, two individual visitor patterns turn queries from the Query API into database specific query batches. The first visitor pattern translates property references into column references and parameterizes literal values. The second visitor pattern handles the actual creation of T-SQL from the query's data structure. The DataInterface class, which handles transactional interaction with the database, will take these generic queries and utilize the visitors to get a command object and execute it against the database.

LoadInstruction

The last piece that's important in understanding how DatabaseRepository 'does its thing' is the LoadInstruction. In functions where we're fetching from the database, we wanted to provide the option to allow the consumer to have a lot of control over what comes back and how much. This is where search criteria, paging options, which properties to load, which child types to load and how to load each child type gets specified. While the default is to only load the requested type, without paging and populate all fields of the type; LoadInstruction gives consumers the option to really tailor the result to exactly what they want from the database.

Limitations

Hopefully none of these are deal breakers but there are things DatabaseRepository will not do. I'm sure some developers will be annoyed by the list but there's just some things an ORM should never do.

Updates and Deletes don't work without a where clause specified. You'll actually get an exception if you try.

Nvigorate will not update primary keys. This is partly because of the risk of breaking relationships in databases without FK constraints or getting exceptions in databases that DO have FK constraints.

The updates will work in an optimistically concurrent fashion. An upcoming feature is to compare the number of affected rows against the expected number of affected rows and notify the consumer of the disparity. This isn't a great fix simply because in cases where you're persisting multiple instances at once, how would Nvigorate know which row failed? To me, concurrency is really more of an application specific issue because it really depends more on how the application is used.

Expression trees are type tyrants. Nvigorate does have a very robust type casting strategy, however, DBNulls will wreak havoc in some circumstances. If you want to crap up your database with nulls, that's your choice; the binding engine may choke on them.

Getting the total count of available rows from the database in paging scenarios worked in the previous version of Nvigorate. It's going to be a lot more difficult in the newer version because it will support different paging models at each level of the hierarchy. This means that you can page the collection of Employee 5 records at a time while paging each Employee's collection of PayChecks 10 records at a time. Getting the total count is technically feasible but implementing it is going to be challenging and time consuming.

All Done

Hopefully this very high-level discussion of the ORM functionality which is (relatively speaking) nearing completion has got you more interested in Nvigorate. I'll have several more posts in the near future as more features are code complete. I'll also be refreshing CodePlex with this code at some point in the next month or so.

Tags:

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading