Yeah, this can get confusing. It helps to understand how things are working (at least it helped me). The first thing to remember/understand is that the BO is essentially disconnected from the database, thus when you create a new record in the BO, it isn't talking to the database just yet. As I understand it, the process of creating a new row is something like this (kind of medium level view) when you add a new record to a BO from a maintenance form:
1. Add() is called on BO.
2. BO creates a DataRow and adds it to the underlying DataTable within the BO (standard ADO stuff). The values of the items in the row are all NULL.
3. The SetDefaultValues event is raised. You can set the values of items within the row at this time,
before anybody will be able to access them.
4. The BO will update any bound controls. During this process the strongly typed properties of the BO are accessed. It is here that we will get errors if the value within the underlying DataRow is NULL and the property is just doing a straight access of that item. I.e. if the property is an int, and the value in the DataRow is NULL, a invalid casting exception is thrown as .NET can't cast a NULL into an int. The code would look something like this (this would be in a property called MyColumn):
'-- Vb version...bombs if value in DataRow is Null
Return CType(Me.CurrentDataRow.Item("MyColumn"), Integer)
// C# version...bombs if value in DataRow is Null
return (int)this.CurrentDataRow.Item["MyColumn"];
When you setup the Return Alternate on Null or Use NullableGeneric options in the BO mapper, all it is doing is adding code to the property Get/Set (depending on setup) that handles the value being null in the DataRow. Something like this when you use Return Alternate on Null:
'-- Vb version...handles value in DataRow being Null
Dim value As Object = Me.CurrentDataRow.Item("MyColumn")
If value Is DbNull.Value Then
Return String.Empty
Else
Return CType(Me.CurrentDataRow.Item("MyColumn"), Integer)
End If
// C# version...handles value in DataRow being Null
Object value = this.CurrentDataRow.Item["MyColumn"];
if (value = DbNull.Value) {
return String.Empty;
} else {
return (int)this.CurrentDataRow.Item["MyColumn"];
}
5. The user then enters information into some UI control.
6. Via binding, the value in the BO is updated. This is done by accessing the strongly typed properties and would use code something like:
'-- VB version
Me.CurrentDataRow.Item("MyColumn") = value
//-- C# version
this.CurrentDataRow.Item["MyColumn"] = value;
You'll never get into trouble here (unless the property has a rule to not allow nulls, in which case the BO would handle raising that error and notifying the user), because the value within the DataRow is an object...it accepts anything. If you setup the field to set a null on alternate value, the code changes a bit to:
'-- VB version
If value <> String.Empty Then
Me.CurrentDataRow.Item("MyColumn") = value
Else
Me.CurrentDataRow.Item("MyColumn") = String.Empty
End If
Me.CurrentDataRow.Item("MyColumn") = value
//-- C# version
if (value != String.Empty) {
this.CurrentDataRow.Item["MyColumn"] = value;
} else {
this.CurrentDataRow.Item["MyColumn"] = String.Empty;
}
7. The user continues to enter data into bound controls, resulting in updating of the BO's underlying DataTable to have the appropriate values.
8. The Save button is clicked (or Save() is called).
9. The BO the produces a SQL call, which could be either a SQL statement or a call to a sproc. It will include the values of every field in the BO (minus those excluded via the FieldsToExcludeFromInsert property).
10. The SQL call is sent to SQL server to be executed. This is where any defaults setup at the db level come into play (and triggers, constraints, etc.). As you can see, the BO won't even know that these defaults have been set until it is refreshed with the data for this record (I'm a little fuzzy on if that happens automatically or if you'd need to make a call to have it happen).
Based on how I understand this works, you can see that even if you have defaults set at the db, the BO will throw an exception long before the db has a chance to set that default. This means that you
have to also set defaults within the SetDefaultValues event handler. Now, the code posted by Peter at
http://forum.strataframe.net/Topic10223-6-1.aspx shows a nice way to set those defaults from the defaults defined within the db. Kind of the best of both worlds.
I hope that helps you understand how defaults work (this is of course assuming I got the process pretty close to right).