Wednesday, March 01, 2006
« Authoring tools | Main | Should validation be in the UI or in bus... »

This recent MSDN article talks about SPOIL: Stored Procedure Object Interface Layer.

This is an interesting, and generally good idea as I see it. Unfortunately this team, like most of Microsoft, apparently just doesn't understand the concept of data hiding in OO. SPOIL allows you to use your object's properties as data elements for a stored procedure call, which is great as long as you only have public read/write properties. But data hiding requires that you will have some private fields that simply aren't exposed as public read/write properties. If SPOIL supported using fields as data elements for a stored procedure call it would be totally awesome!

The same is true for LINQ. It works against public read/write properties, which means it is totally useless if you want to use it to load "real" objects that employ basic concepts like encapsulation and data hiding. Oh sure, you can use LINQ (well, dlinq really) to load a DTO (data transfer object - an object with only public read/write properties and no business logic) and then copy the data from the DTO into your real object. Or you could try to use the DTO as the "data container" inside your real object rather than using private fields. But frankly those options introduce complexity that should be simply unnecessary...

While it is true that loading private fields requires reflection - Microsoft could solve this. They do own the CLR after all... It is surely within their power to provide a truly good solution to the problem, that supports data mapping and also allows for key OO concepts like encapsulation and data hiding.


Wednesday, March 01, 2006 11:45:11 AM (Central Standard Time, UTC-06:00)
If you were to expose your business objects only through an interface (primarily method based for changing data, and getters), without letting consumer code reference the implementation, then it wouldn't be so bad.

But... I agree with you. Microsoft could have done better.
Wednesday, March 01, 2006 2:10:18 PM (Central Standard Time, UTC-06:00)
But what you suggest is to abandon the beautiful OO support provided by .NET and return to a COM-like world of pure interfaces. Ugh!

I want my wonderful, true OO world in .NET - I just want some high-performance automation around data access to exist in that same world. It doesn't SEEM like too much to ask... :)
Wednesday, March 01, 2006 2:44:30 PM (Central Standard Time, UTC-06:00)
Why not do it the same way we do serialization? That seems to have solved the public/private issue.
Jonathan Allen
Thursday, March 02, 2006 8:00:06 AM (Central Standard Time, UTC-06:00)
I think, the interface is the right way, but another way as you said.
if the SPOIL fill the object thru an explicit interface (or get the parameters from there) these would not be seen if you access it the normal way. so they are just "hidden". i think thats an fine solution.
Christoph Richter
Thursday, March 02, 2006 9:14:21 AM (Central Standard Time, UTC-06:00)
True, allowing SPOIL to load the object through an interface would be a workable solution. Though it would have to be a custom interface per object, and thus would require extra coding for any properties that aren't read-write. It would also require extra coding for any properties that have business, validation or authorization rules (which is most of them). So really you'd have to duplicate every property as you create the object - doubling the code you need to write. Ugh!

Another really bad thing about this approach is that it means your object's data would be exposed in an unprotected manner through this interface. How much do you trust your fellow developers? Your UI developers? Are they _really_ going to avoid the temptation to bypass your business logic and use that other interface? If the object seems "hard to use", but becomes "easy to use" through the interface, which way do you think they'll go?

While I agree that such an interface (other than doubling the code in a typical class) is a workable solution, that idea still breaks encapsulation and makes it trivial to bypass data hiding.
Thursday, March 02, 2006 9:25:14 AM (Central Standard Time, UTC-06:00)
I have attempted to implement this solution sort of. Not using stored procedures but I am building Sql queries with parameters. I ended up using reflection to view the private variables and created a couple of attributes to be able to rename/uninclude fields from being used. The only issue I ran into was that in this case, there are some private variables I want to set directly and some public properties I want to set for reasons of business rules. Other than that, I don't really write any Sql access at all. It's pretty neat stuff. The only way I have thought of to deal with the field/property set is to check if the PropertyInfo.HasSet is true. Only thing is, I have to stick to a naming convention bewteen the field and property so they can map. Kind of cheesy but I could take care of that as well with a custom attribute. Writing the Sql is one of the most boring and necessary parts of creating a project and MS could have done much more to take care of this!
Joe Seymour
Monday, March 06, 2006 9:17:58 AM (Central Standard Time, UTC-06:00)
Back when I was writing CSLA .NET 1.0 I built a prototype for a totally generic DAL. It used reflection to load all the fields, and relied on mapping data to map between the db column names and the field names. It worked nicely.

In my perf tests there was about a 15% perf hit by using this scheme instead of loading the fields directly inside the object. _Knowing_ that I'd be slammed by perf-hungry readers if I went down that road, I opted instead to take the path of putting the data code inside the objects to get the higher performance. And of course there's that really nice side effect of preserving the OO concept of encapsulation ;)

But when you think about it, there are probably a great many applications that could absorb a 15% perf hit to avoid having to write and maintain that data access code.
Thursday, March 09, 2006 7:01:26 AM (Central Standard Time, UTC-06:00)
Rocky,
I was just curious to know whether you have looked at NHibernate at all as a tool for providing a generic ORM/DAL in conjunction with CSLA?

It seems from the investigation work we've done so far that it allows you to preserve your object model and also eliminate the need to write all the SQL/SPs as part of the project itself.

It also works against fields, rather than properties.

Any thoughts?
David Dilworth
Monday, March 13, 2006 5:28:38 PM (Central Standard Time, UTC-06:00)
[Another really bad thing about this approach is that it means your object's data would be exposed in an unprotected manner through this interface. How much do you trust your fellow developers? Your UI developers? Are they _really_ going to avoid the temptation to bypass your business logic and use that other interface? If the object seems "hard to use", but becomes "easy to use" through the interface, which way do you think they'll go?]

One way to bypass that issue is to utilize the StrongNameIdentityPermissionAttribute on said interface members. An imperfect solution but easy to use if you tend to release only strongly named assemblies (IMHO a good idea regardless of GACing).

As a BSP (blatent self promotion) we've also created a GDN community aimed at enhancing and running with the SPOIL concept. Anyone wishing can find out more information here: http://www.gotdotnet.com/Workspaces/Workspace.aspx?id=d70f8668-bb98-4270-9f0f-ba166776f3ac
Jimmy Zimms
Thursday, April 06, 2006 8:22:17 AM (Central Standard Time, UTC-06:00)
What surprised me the most about SPOIL is that it's exactly what I published way back in August 2002 in my MSDN Magazine article [1] titled, "Dynamically Bind Your Data Layer to Stored Procedures and SQL Commands Using .NET Metadata and Reflection"! Heck, even the class names and some of the comments have remained the same. I guess they just took off from where I left and added a few helper methods to run the commands, but I think it's just the wrong approach. I deliberately never provided any helpers for executing the commands because it's a different problem space althogether. You want to help in fabricating the command objects and that's it (do one thing, do it well and leave the rest to upper layers or subsystems). Let the caller decide on what to do with the commands, how to execute them and what to do with the result sets. You don't want to assume the caller wants ExecuteScalar, ExecuteNonQuery or ExecuteReader. May be someone just wants a DataSet by connecting a command to a data adapter and then doing a fill? With ADO.NET 2.0, you might be interested in asynchronous execution. What about stored procedures with multiple result sets? Or event XML with ExecuteXmlReader? The point is, you simply can't satisfy all combinations with providing a hundred overloads. Something case will always be forgotten.

The work that I originally published in the MSDN Magazine article was a precursor to DBMethod [2] in which I solved all of the problems I mentioned at the end of article. From a design stand-point, you might be interested in checking out the discussion "A Design Pattern for Data Access Methods" in the introductory article on DBMethods [3]. Even with all the helpers, the real problem that SPOIL doesn't go out to solve is reduce the time needed to define signatures for the 200 stored procedures in your database and then keeping them in sync. This is something I addressed with DBMethods by reading SQL metadata and generating signatures for VB.NET or C#. This can be done using a command-line tool or a Visual Studio custom tool for an integrated experience. DBMethods also internally uses runtime MSIL generation to contain the cost of reflection to initialization-time only. Anyway, I don't want to turn this into a plug or anything. My point is that something like SPOIL and SPOIL+ are not adding much value. The "transform your code from an object-orientation to a relational paradigm and back " is the wrong problem statement and therefore falls short on the promise. It would have been simpler and more right to say that you just want to reduce all that boilerplate code for setting up command objects by making database stored procedures appear like methods from managed world. Finally, by making sure that you never execute the generated command but just return it, you don't need to provide integration with DAAB, EntLib or what have you. The SqlCommand is the lowest common denominator between all libraries, including well, ADO.NET!

[1] http://msdn.microsoft.com/msdnmag/issues/02/08/NETReflection/default.aspx
[2] http://www.raboof.com/Projects/DBMethods
[3] http://www.raboof.com/Projects/DBMethods/Introduction.aspx
Comments are closed.