Archive for category CSLA.NET
In a post from a long time ago, Eric Gunnerson asks about persisting complex objects. I’m working on a project that is using the CSLA.NET framework. One of the early decisions we made was how to get data updates back to the database. We chose to use Eric’s preferred method, which is change tracking. Eric also likes using serialization. That’s built into CSLA.NET (as well as .NET, of course), so we didn’t have to make a deliberate decision to use serialization.
One decision to make with persisting the objects is the actual mechanism for the updates. We ruled out using a dataset because we wanted to avoid the extra overhead that comes with a dataset. The examples from Rocky’s book all use stored procedures. The stored procedures have to have a parameter for every field. This means you are passing every field to the database, even when they did not change. Another option is dynamic SQL.
I had done some research in the past on the debate over stored procedures vs. dynamic SQL. One of the key issues in the debate is controlling access to the database. In some environments, dynamic SQL might mean that you have to give users access to all tables, and that might not be desirable. With an internal business application, the security can be handled through account impersonation in Enterprise Services or ASP.NET. Only one account needs to be given access to the database. In our environment, dynamic SQL seemed like a viable choice.
When profiling updates with “old-fashioned” ADO Recordsets, I observed that the recordsets generate dynamic SQL. The SQL was bloated, but reliable, and worked well with the optimistic concurrency model. I wanted to use this model, but in a more efficient manner.
In its simplest form, here is the format of a SQL statement that updates a record and also provides optimistic concurrency protection:
Set Field1 = NewValue
Where PrimaryKey = KeyValue And Field1 = OriginalValue
With our decision to use dynamic SQL inside the business object, the business object will already have most of the information needed to generate this statement. It has to know what table(s) are used to load the data. It has to know what columns are used to load the data, including the primary key value. It has to know the current value to update the database. The only optional piece is the original value. The original value is usually overwritten when the user makes changes.
One option to manage this is to double the number of variables. For each m_Field1, you would also have an m_orig_Field1, for example. This technique forces the business object developer to write two lines of code when setting each field value.
Since the update statement could be manufactured mechanically with all the necessary information, we felt there should be a way to encapsulate the necessary information. We have created “smart data” classes for the basic data types (string, integer, double, date, and boolean). Each smart data type knows the column name it is associated with, the original database value, and the current value. It turns out this is enough for efficient updates.
These smart data objects share a common interface. The interface defines three read-only properties: Column name, original value, and current value. A DataUpdater object uses this interface to generate the update statement.
Within the business object, the following lines of code are used to update a sample business object:
Dim updater As New DataUpdater(“MyTable”, m_KeyValue)
Within the updater, the SQL statement is generated and run against the database. As an added bonus, the smart data objects which have a concept of an “empty” value (i.e. string and date) also handle translating the empty value into a null value for the database.
The smart data objects are also easy to use when fetching the data. Before the smart data objects, fetch code looked like “m_Field1 = dr.GetString(“Field1″). dr refers to a SafeDataReader, which is part of the CSLA.NET framework. With smart data, the line is written as m_Field1 = New SmartString(dr, “Field1″).
The smart data object handles all the details of retrieving the value from the data reader and setting the original and current values.
We’ve added other capabilities to the smart data objects that help our Windows Forms classes, too. But that is outside of the persistence topic. These smart data objects are working very well for us. They do create more overhead, but nothing like a dataset. If you have a similar environment, perhaps you can benefit from this concept as well.
Note to self: Check out thie code in this forum message regarding an automated way to generate databinding.
An interesting discussion is going on in the CSLA.NET forum. In message 5 of the discussion, Tom Leylan points out that the discussion is getting bogged down in semantics. I would agree with Tom’s assessment. I was going to post a response of my own to his original entry. However, I realized halfway through my response that there could be multiple interpretations of the term “parent”. I canceled my response and elected to seehow others interpreted it.
I think semantics is the root cause for a significant percentage of software bugs. I also believe that these kinds of bugs are harder to detect. The main problem is that human language is very inexact. Programming languages are the exact opposite. We try to build computer programs by translating the inexactness of human language into the exactness of a programming language. Translations of this type are very, very, difficult, even for the most powerful computers on earth — our brains.
Conversely, translating from one exact language to another exact language is very easy. There are hundreds of programs to translate code from one programming language to another. In a sense, every compiler is simply a translator, since a compiler is translating a higher-level language to a lower-level language. Compilers are introduced at the undergraduate level, so they can’t be considered a terribly difficult concept. :-)
An assembler is translating assembly language to machine language. In the .NET environment, two translations are taking place. There is a compiler that translates VB, C#, J#, etc. into IL. A second compiler translates the IL into machine language.
The task is not so easy when one wants to translate English to Spanish. In fact, it’s not so easy translating from relatively similar languages. Many American jokes don’t translate very well when told in the UK, even though both countries use English as their primary language. Try using Babelfish to translate this page into French.
If you are interested in how the best and brightest are dealing with this issue, check out the Proteus project.
Last night, I installed a test project called ActiveTracker. You’ll have to have an account to the MSN group to access the link. ActiveTracker is a modification by Petar Kozul to Rocky’s ProjectTracker. ProjectTracker demonstrates many of the features of the CSLA.NET framework. ActiveTracker includes an implementation of the Observer design pattern.
It was late by the time I successfully built the solution, so I was only able to play around with the general functionality for a few minutes.
One of the Oberver features is implemented in the ProjectList dialog. The dialog lists the projects. If a project is added, the list automatically refreshes and shows the new project. This is a feature that I think can make a system great. Keeping the UI updated with the most current information can be a great benefit. If a user A is reviewing a customer order and user B changes the customer order, the display for user A is automatically updated. I love this concept.
I decided to test the active project list by opening up two instances of the application. I opened the list in the first instance. I added a new project in the second instance. It did not automtically refresh the first instance. I added another project in the first instance, and the display was refreshed, properly showing all the projects.
Every time I have read about the MVC and Observer patterns, the examples have always dealt with “in-process” notifications. I want to cross that boundary! I’ll be looking at the project code over the weekend and try to figure out how it could be modified to register Observers from other processes (and other computers). I think the biggest design decision will be the how to change the communication channel. Isn’t this just like a chat program under the covers?
In an ideal world, all business objects would be synchronized with each other. With CSLA.NET, what would be a good way to handle notifying other objects that an object has changed?
The best idea I can think of is to have a central “change message” queue. In its simplest form, all clients would register with the central message server. When they receive events, they would determine whether the message was meant for them or not. If it was, they can iniitate whatever action is necessary to deal with the change.
When dealing with child collections (say lines on an order), we often want the parent object to take action when changes occur in the child object.
Check out this discussion.
One of the issues we have to deal with when implementing CSLA.NET is generating business code for all the existing database tables in the system. Much of this business code is repetitive and formulaic. A code generator would be very helpful.
I was reading an article by Rockford Lhotka about concurrency techniques that can be implemented within CSLA.NET.
With disconnected ADO recordsets, you automatically get a nice set of tools for optimistic concurrency. With ADO.NET, you can have the same thing with datasets. However, the CSLA.NET architecture doesn’t hold a dataset in the business object. This means you have to implement optimistic concurrency on your own.
Rocky suggests in the article that implementing field-level concurrency involves three sets of variables. One set keeps track of the original values. A second set keeps track of the current values edited by the user. A third set is filled with the current database values before you attempt to save the data to the database.
The example in the article implies a series of “If” statements to look for concurrency violations. My concern with this is when we add properties, we have to also add code to the concurrency checking for that field. I’d like to automate the concurrency checking.
Perhaps the concurrency checking could be automated with reflection. During the fetch process, reflection could be used to load a list of original values.
During the update process, a “current” copy of the business object could be loaded. Using reflection, the properties of the of “current” business object could be compared to the original values. The properties of the “edited” business object could also be compared to the original values. If both are different we have a concurrency violation.
This adds a lot of overhead, so performance might be a problem.
One common business requirement is being able to audit important business changes. This means that of the 20 fields that are enterable on a customer record, we care about tracking 5 of them.
There’ve a been a few threads in the CSLA.NET discussion group about creating audit log and audit entry objects. These are absolutely necessary.
What about creating a custom attribute to assign to fields that need to be audited? Perhaps this attribute could be targeted to a class as well, so all fields would be audited?
Should the auditing records only be created when changes are applied? I think this would require the ability to do a comparison of old/new on every field. That could be a lot of overhead, although the comparison would only be necessary on the audited fields.
Should the auditing be recorded as changes are made and included in the state of the object? This could significantly increase the size of the business object.
I’ve started investigating an application framework called CSLA.NET. This framework comes from the book Expert One-on-one Visual Basic.NET Business Objects. As I’ve been reading the book, I’ve been conceiving of how we could implement this framework at work. The ideas have been coming to me at the oddest times. I’ve decided I’ll make entries in the blog relating to these thoughts.
At first, these entries are going to be general in nature. I’m not even 1/4 of the way through the book and I haven’t looked at any code other than the samples in the book. As I dig into the framework in depth, I’m sure the thoughts I have will become more detailed and include code of my own.