How do you decide what features to add or cut?

Ever since my WilsonORMapper hit v3.0 back a few months ago things have been very smooth.  By that I mean both that there have been very few bugs and very few new feature requests.  In other words, its reached a mature point and it meets most expectations.  Lately I've been readying v3.1, and I had to make some decisions on what to include.

Some feature requests are easy to decide to include -- they are easy to code, affect little else, and are often requested.  Examples of this were the desires to map properties (as opposed to just member fields) and to have a public ExecuteScalar method.  Note that I actually don't like mapping properties and ExecuteScalar isn't really necessary, but they were still included.  There were also some other requests that were easy to decide to include, even though they were not often requested.  These were still easy to code and affected little else, but they also provided some real value even if they were seldom requested.  Some examples of these were adding support for multiple mapping files (or multiple embedded resources) and output parameters for stored procs.

Next there were a few requests that were easy to not include -- these aren't easy to categorize, so lets look at examples.  One case was Whidbey generics and nullable types -- these were often requested, and they may be easy to code and affect little else.  But's let be realistic -- these are still in beta 1, changes may be possible, and few really need them yet.  But note that these will be one of my top priorities for v4.0, probably in the beta 2 timeframe when there is a go-live license.  Another case is that a few people don't agree with my assumption about unchanging primary keys, also called surrogate keys.  I try not to force my personal tastes on others, thus I decided to allow properties to be mapped now, but this is one assumption that is to integral to my mapper.  I hesitate to say that those that disagree are doing something wrong, but there must be a few basic assumptions, especially with a "simple" product.

But then there are the requests that are hard to decide on whether they should be included or not -- these are especially difficult when people send you code.  My mapper does support entity inheritance, but only the most minimal database inheritance -- this would be a huge plus to add to my list of features.  But this is a big change, probably affecting a lot, no one has sent me any code, and this is a minor point update.  So this did not make the cut -- it might make sense in v4.0 though.  Another thing my mapper supports is composite keys, but not composite key relationships -- at least not until now.  This was also a big change, and it certainly did affect a lot, but someone did send me some code on this one.  Now I should point out that just because someone sends me code does not mean its done -- someone else's code usually solves their cases, but not all the other generic cases, so it can still be a lot of work.

Finally, there was one case that really required me to make a difficult call -- one-to-one relationships.  My mapper supports one-to-many, many-to-one, and many-to-many relationships, but not one-to-one relationships.  This is not trivial to implement, and it also affects a lot of things, but it would be a big plus on my feature list -- and someone sent me some code!  But this did NOT make the cut -- that's right one-to-one relationships are still not supported by my mapper, and likely never will.  Why you ask?  First, there is an easy work-around -- one side of a one-to-one relation is actually a many-to-one relation, and the other side is a one-to-many relation where the many is always equal to one!  If you don't like to see that, then that's what a property is for -- leave the member field an IList, but make the property be the strongly typed object with the getter and setter hiding the fact that you are actually always working with the 0th index object in a list.

But isn't this requested enough to justify making it easier?  That's where the hard call came in -- and I decided that it is not worth the additional complexity.  That would be one more thing to have to explain on the end-use side, and it would complicate the codebase greatly.  That's because every relationship type has to be handled for new and existing objects, lazy-loaded and not, dynamic sql and stored procs, and now for single and composite keys.  That's a lot of cases -- a lot to code (the code sent me handled only the few pertinent to that person), a lot to test (I still haven't tested all the cases of composite key relationships), and a lot for the next person to worry about!  And that last part is one of the most important things for my mapper -- the simplicity of the codebase itself.  This is why I get so many user contributions -- they find it easy to extend when there is something else they want.

So I have consciously chosen to keep my mapper "simple", although I think that I can safely say that it does meet most people's needs already -- far beyond the most common 80-90% that I was originally shooting for.  By the way, this is also the one of the few things that I think still makes my mapper stand out as unique against the likes of NHibernate, LLBLGen Pro, and EntityBroker.  The others may have more features, and NHibernate is open source, but just try to use any of these others for the first time in 30 minutes, or just try to extend any of those to have a new feature you desire.  Of course the other main thing mine has to offer is provider support -- I don't think any other can claim to support so many databases.  And that's not just a claim -- its also a reality that many have proven -- a reality that is possible primarily due to simplicity and not targetting every possible feature.

2 Comments

  • Do ORMappers have a place in a closed shop's ASP.NET application running strictly MS SQL server?

  • Travis: of course! Unless you want to write all that code yourself ;)



    " By the way, this is also the one of the few things that I think still makes my mapper stand out as unique against the likes of NHibernate, LLBLGen Pro, and EntityBroker. The others may have more features, and NHibernate is open source, but just try to use any of these others for the first time in 30 minutes, or just try to extend any of those to have a new feature you desire. "

    If you think your mapper is easier to use than mine you're clearly mistaken. For example, LLBLGen Pro doesn't require you to write mapping files, the generated code is ready to use and sports all the functionality you want, so you don't need to jump through hoops.



    Furthermore, I add new features every 2 months, you do too? LLBLGen Pro has a template system in place with multiple template languages and a task-based code generator which is more flexible than anyone of the competition. You can extend the code generator to extend the code you're going to work with with easy classes or templates. You want a unique filter element to work with the predicates used to filter on objects? Inherit from a base class and add 20 lines of code and you can use your own filter element in your code. For example full text search on MySql.



    "Of course the other main thing mine has to offer is provider support -- I don't think any other can claim to support so many databases. And that's not just a claim -- its also a reality that many have proven -- a reality that is possible primarily due to simplicity and not targetting every possible feature."

    How many databases do you really support, Paul? I mean SQL-92 inserts with no real functionality besides simple inserts... that's not that hard. Do you support sequences on oracle, sequences + identity on DB2, sequences on firebird? Do you support delete from from / update table from table on oracle/db2/mysql/firebird/access ? Can you do multiple joins of the same table with aliasses and filters targeting those alaisses (SQL 101) on oracle 8i AND for example sqlserver/db2 ? (so you have to implement ansi and non-ansi joins? ) Do you support aggregates, group by, having clauses on 'all these databases' ?



    You support just the very very simple stuff, of course you can then support a lot of databases. The non-ansi joins on oracle 8i alone break any generic db driver code. Support a lot of databases with REAL functionality requires some more code than a set of simple rules in a provider definition, Paul and you know that. The fact alone that you don't support 1:1 relations proves the fact that the code base is very simple. You don't deny that, but at the same time you praise yourself with phrases like that you support the largest amount of databases. I think it then depends on what your definition of 'support' really is.



    Btw, we too use a provider model, both for the schema driver and for the DQE engine, and so does NHibernate.

Comments have been disabled for this content.