Common practice, allowing null values in data classes.

  • Thread starter Thread starter D Witherspoon
  • Start date Start date
D

D Witherspoon

What is the accepted method of creating a data class or business rules
object class with properties that will allow the returning of null values?

For example... I have a class named CResults with the following properties.

TestID int
QuestionID int
AnswerID int

So, this is a simple example, but I want to be able to know if AnswerID is
null. I don't want to know that it is 0 or anything else. I want to be
able to have it return null (instead of the default "nothing" value of 0)

This is not my real world example, it is just an example I'm using to
explain what I want to do. I am developing a large scale application and
the properties of the classes can contain null or legitmate values. If
there is an integer property I want to know if it was 0 or null. There is a
huge difference, as 0 implies that literally 0 was entered into the
database, and null is that nothing has been entered in the database.

How can I design my data classes using VB.NET to take this into account.
The intrinsic datatypes in VB (integer, long, string, etc) can not have null
values.

What is common practice in this case?
 
One way would be to define your properties as SQL Types, they have an IsNull
property, I didn't like this I use strings instead, that way I can format it
however I want when I pull from and put into the database, it the string is
empty I put a null in the database or expect that value to be null in the
database. It works ok for me...
 
What I usually do is have another property that indicates whether the value is
valid or not. For example, you could add a property "HasAnswerID" that would
return true if there is a valid (not null) AnswerID value and false if the
AnswerID value is null.

The AnswerID property would always return an int value, with some default value
if the underlying data is null.

Another option is to return an object value set to null if the underlying data
is null and to the integer value if it is not. The problem is that you have to
cast it as an int before you can use it.
 
D,

An integer can be nothing however has than forever the value 0.

So I think that when you would use the integer for two purposes
1 the value
2 to evaluate
That the solution can be to make it not an integer however an object.

(Where I assume that your busines object is not a datatable where this is in
the non typed format build in).

Just my thought,

Cor
 
Thanks to everyone for their assistance.

I have come up with a different solution than the ones you guys mentioned.

I am going to create a class object, maybe called CInteger and have 2
properties. Value, and IsNull.
This will be the return type for my integer fields.

I don't like using SQL data types as the idea for my data classes is for
them to be generic and not tied to any particular DBMS data types.

This is the best idea I could come up with. Let me know what you guys
think.
 
From what I understood is that the return types of all of the properties
would be of Object. I'd like to specify the type of value being returned or
passed to the property.
 
Cor,

Your solution is really inefficient since many of the types are value types
and using the Object type requires a great deal of boxing and unboxing.

--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
 
Nick,
Your solution is really inefficient since many of the types are value types
and using the Object type requires a great deal of boxing and unboxing.

However not as inefficient as the now chosen method.

It seems that boxing is often a big hit by a lot of people, and I don't know
why.

Boxing and unboxing is a standard (ILS) procedure. When this kind of things
become important than you should start with avoiding properties and use
direct the values again, with that you will probably gain much more
performance than avoiding boxing and unboxing. (Not that I shall advice
that).

(By the way have a look at the datarow, in my opinion will you you will see
there the same approach)

Just my thought,

Cor
 
Nick,

And too proof what I wrote, try this sample beneath.
I am curious about your answer,

Cor

\\\
Public Class Mytest
Public Shared Sub Main()
Dim start As Integer = Environment.TickCount
For i As Integer = 0 To 10000000
Dim a As New hulpclass
If a.myval Is Nothing Then
a.myval = 0 'Box
Else
a.myval = 1 'Box
End If
If CInt(a.myval) = 0 Then 'Unboxing
a.myval = 0 'Box
Else
a.myval = 1 'Box
End If
Next
Dim ends As Integer = Environment.TickCount - start
MessageBox.Show("Processing this routine 10.000.000 times cost: " &
ends.ToString _
& " Milliseconds")
End Sub
End Class
Public Class hulpclass
Private m As Object
Public Property myval() As Object
Get
Return m
End Get
Set(ByVal Value As Object)
m = CInt(Value)
End Set
End Property
End Class
///
 
Hi Cor,

Measurements work by comparison. I see that you are measuring the amount of
time it takes to run through some box and unbox routines (not quite
perfect... your assignment results in boxing the value, unboxing it, boxing
it again).

I don't see a comparison to the amount of time it would take to use a
SQLInt32 to store a number that contains an "isnull" column.


--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
 
"Nick Malik [Microsoft]"
Measurements work by comparison.

No measurments works by taking the zero point and than measure what is the
distance/weight or whatever from that. I think you are mixing things up.
I see that you are measuring the amount of time it takes to run through
some box and unbox routines >your assignment results in boxing the value,
unboxing it, boxing it again).

Exact that you have seen well, that was what I was after. When you take the
simple short time, (maximum one minute for a pro) too test it, than you can
see why I took more time to make it. The most important reason was trying to
help you to overcome that dogma of boxing. That seems as a strange
prejudiced idea in the mind of some people.
(not quite perfect... )

May I know what is the intention from this extra sentence in your message. I
hope it is not personal meant, because I did not see that I did something to
you, when that is the fact, feel at such a moment to tell me, because that
is never intended.
I don't see a comparison to the amount of time it would take to use a
SQLInt32 to store a number that contains an "isnull" column.

That was not what I was after, however you can take my sample to show what
you can win with that.

I am not able to use a SQLInt32 direct as a value in a program, I know how
to do that, however that is exactly in the way I showed.

And because of that last fact am I *very* curious for another method and
will be very glad when you make that. (I wrote that not earlier in this
thread, because I was not wanting to discuss that).

Cor
 
Hi Cor,

I took your challenge and wrote my own timing app. I used the HiPerfTimer
class from CodeProject and ran a simply box/unbox operation from 10,000
SqlInt32 values where about 20% of the SqlInt32 values were nulls. (to
simulate a normal situation).

I created two classes: one that used an internal "object" type value and
would allow the caller to test if the object is null, the other used two
internal values, one of type int, and the other of type boolean, to meet a
common interface.

It was interesting. The first time I ran it, the class based on storing
values as an object ran in about 0.0073 seconds. However, simply running
the test again (on the same data) returned about 0.0038 seconds. Crazy! On
the other hand, the class based on two integral types ran in about 0.0033
seconds every time.

(I keep saying "about X seconds" because I ran each test many times. The
numbers were slightly different each time, but they average out to the
number presented).

So, for 10,000 box/unbox operations, there is a performance hit, but it is
much smaller than I expected. Even assuming worst case, the difference is
so small that it is not likely to be human-percievable.

So... you are right. The difference is probably inconsequential from a
performance standpoint. Readability is another issue (and a different
discussion). And, then, there's personal preference. It appears that these
factors may weigh greater when determining which way a developer SHOULD go.

I'll see if I can get my little comparison app uploaded to GotDotNet or
CodeProject in the next day or so, to allow others to take a look.

I learned something today. Thank you Cor.

--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
 
Another method is to fill int.MinValue or int.MaxValue to int property
AnswerID . So if you nead to check if db returned null just check if AnswerID
property is int.MinValue.

When passing value to stored procedure parameter just check again is it
int.MinValue, and if it is just pass DBNull.Value (I usually use IIf function
in VB.NET or ( ? :) syntax in C#)

The same way you could use for other types (especially for DateTime,
Decimal, ...)

This way performances will not be affected.

Boris
 
Back
Top