How do you decide what features to add or cut?
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.