I want to give a very special thanks to several significant contributions from subscribers for the just released WilsonORMapper Version 2.0:
1) Ken Muse participated in lengthy design discussions, gave lots of sample code, and helped with a lot of testing for the major new features of Recursive Persistence and Cascading Deletes. We ended up staying as close as possible once again to the MS ObjectSpaces syntax, including using an enum in PersistChanges and using the cascadeDelete mapping attribute for relations.
2) Allan Ritchie provided code that became GetCollection, for your own strongly typed custom collections, as well as single-handedly adding the feature for using embedded resources for the mappings. The GetCollection method takes an extra parameter to allow you to specify your custom collection type, about as close as we can get to Generics without actually having them in .NET v1.1!
3) Jason Mesches, one of Kevin McNeish's developers at Oakleaf Enterprises, wrote the OPath converter that is now available in the QueryHelper's GetExpression method. Kevin also has Rick Strahl and John Miller adding support for the WilsonORMapper to his Mere Mortal's Framework -- so if you need a complete framework to get up and running quickly, I strongly recommend theirs.
4) Jerry Shea added some support for class hierarchies and simple mapping inheritance, as well as making the ObjectHolder's InnerObject property settable.
5) Tim Byng helped me to add support for optional field default null-values, again staying close to the syntax MS ObjectSpaces, back in v1.1.
Thanks to you all, Paul Wilson
I just ordered Test Driven Development in Microsoft .Net by James W. Newkirk and Alexei A. Vorontsov. I've done a little bit of NUnit, but I'm still lost on how to implement it with database apps and other non-algorithmic apps. I haven't bought a book in a good while, since I get more review copies than I can read, but this one looks like a must.
I attended the Atlanta DevDays today and caught up with Jerry and Matthew. While I agree with Jerry that I don't personally get anything much out of these things, other than socializing, I think that's mostly due to the level that we have reached and not just a matter of the content. I actually thought the ASP.NET security track was quite in-depth, although I agreed with Jerry that there wasn't anything new that folks like us shouldn't already know. But there's far too many developers out there that don't have a clue they may need to configure anything in IIS, or that IIS6 in W2K3 works differently, that even the intro hour wasn't “bad” -- it was just boring to us. And I don't think most developers have ever actually had the opportunity to see real hacks being done, which were shown in the second hour, so I think it was probably an eye-opener to most people. So yes, I didn't get much out of it, but I thought it was an excellent day, and I look forward to the MVP Global Summit in a few weeks for my own real education. By the way, I do think that big conferences like TechEd and DevConnections need more advanced sessions, and I've heard those complaints often in the past from attendees over lunch, although apparently the people running those shows don't believe it to be the case.
On another note, I finally got around to listening to Carl and Rory's .NET Rocks show that mentioned me and my O/R Mapper. The guest was Kevin McNeish of OakLeafSD, who's adding persistence to his Mere Mortals Framework, possibly by including my mapper, at least until ObjectSpaces arrives in 2005!
I'll be at the Atlanta DevDays on Tuesday March 16 -- hope to see you there!
I finally got around to adding support for optimistic concurrency to my WilsonORMapper, and it definitely had me think through quite a few design scenarios that I'll share here. Optimistic concurrency works by adding some portion of the record's original values to the where clause of the update statement to make sure no one else has updated it first. Modifying the update is relatively easy, so my issue was that I needed original values. It was tempting to think I had the original values already, since I do track the values, but the problem is that my broker might be used in a multi-user web or distributed app. This means that the values I'm tracking will always be the last known persisted values, which is great for some scenarios, but this does not support optimistic concurrency.
One thing that should be obvious is that I need to store the original values with the actual object, so I set out to explore the various alternatives to achieve that result. One possible solution is relatively trivial -- just require all entity objects to derive from a specific base class which would include support for storing the original values. This is indeed what many other O/R mappers do, but I had originally chosen to avoid the base class approach for a variety of reasons, and I still have the very same objections.
The next possible alternative is to do the reverse -- instead of having a common base, its possible to create a dynamic assembly where all of the entity types are extended. This actually turned out to be very easy to do also, but this would mean that I would have to give up my current simplicity of allowing the explicit new to create objects. This is one of the cornerstones of my mapper approach -- KISS -- Keep It Simple Stupid. So I also abandoned this approach, although I may use it someday for some other feature.
Another possibility is to dynamically modify the existing type using some code injection. The most common way to do this in .NET is to use Aspect Oriented Programming techniques, but this requires not just a base class, but a very heavy base -- ContextBoundObject. Another injection solution is to use the RAIL framework, which looks very promising, but then I noticed there was another problem that I had been overlooking from the beginning.
That problem, which seems to be overlooked by MS ObjectSpaces and most other O/R mappers, is that optimistic concurrency is not necessarily always based on all original values! Its actually quite common to just use a TimeStamp column in MS Sql, and I had already just added the ability to configure fields like TimeStamp to be read-only for someone. Well its just natural to extend this idea and allow the user the configure each field with a persistType that can be either Persist (the default), ReadOnly, or Concurrent. This solves the read-only cases like TimeStamp immediately, but it has the downside of forcing my users to create and configure two fields for other fields that should take part in the optimistic concurrency check -- one each to hold the new and original values.
This is indeed an extra burden on the user, but it has a couple of redeeming values too. First, it allows the simplicity I've strived for to remain in the non-optimistic cases. Next, it allows the flexibility to define your concurrency fields, which is missing from at least some other systems -- great if you are using TimeStamp or if have you have so many fields that you don't want to pass them all -- which was something else I had just dealt with. Finally, my mapper also suports updates with stored procedures, and I don't really see how I could have continued to allow them without somehow also allowing the user to have configurable parameters for both the new and original values anyhow -- so case closed.
I know that I will probably get derided for this solution, since its not very "pretty", but in the end it meets my goals of simplicity for the main cases, which is my strength, while still offering some additional flexibility if you are willing to configure it.
Take a look at http://www.ORMapper.net for the latest on the WilsonORMapper!
Update: WilsonORMapper v184.108.40.206 (3/4/2004) includes the following:
(1) Build simple expressions with an OPath-like syntax using the QueryHelper.
(2) User can define fields for optimistic concurrency, or read-only fields.
(3) Updates can optionally be only the changes, with or without concurrency.