The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

C#/ Validating every string property in the class

Here is a typical situation.

Let's say we have a database with a table T with fields f1, f2, f3 - all strings.

I have a wrapper class that holds these fields (for validation/defaults etc.) so for e.g. I could create an instance of the class

TableT t  = new TableT();
t.f1 = <string>, t.f2 = <string> etc.

I need to check if the assignment is not null, and hold an error message if any field is null. So, we could have a method called "CheckFields()"

if(!t.CheckFields())
  //print t.ErrorMessage;

CheckFields() would look something like this:
{
  if(t1 == String.Empty)
    //Error..
  if(t2 == ...)
    ...
}

etc.

If the table was large with many columns => many properties in the class => more and more cumbersome.

One way to tackle this, from my knowledge of C#, is to "reflect" on this object, get all its properties and then evaluate the value (assuming it is possible). If so, how would we do that?

Secondly - anybody got better ideas?

(PS - I know we could use get/set modifiers to set a default value on assignment, but that's not the intent)


}
Different Approach Send private email
Wednesday, May 04, 2005
 
 
Here's how you would use reflection to read the properties of an object and get the values:

using System;
using System.Reflection;

namespace ReflectionTest
{
  class Program
  {
    [STAThread]
    static void Main(string[] args)
    {
      object o = new TestClass();
      Type t = o.GetType();

      PropertyInfo[] info = t.GetProperties();
      foreach( PropertyInfo i in info )
      {
        Console.WriteLine( "Found property: {0} ({1})", i.Name, i.PropertyType.FullName );
        if( i.PropertyType != typeof(string) )
          continue;

        string value = i.GetValue( o, null ) as String;
        Console.WriteLine( "\tValue: {0}", value );

        if( value == null )
        {
          throw new Exception( "Invalid property value." );
        }
      }

      Console.ReadLine();
    } 
  }

  class TestClass
  {
    public string A
    {
      get{ return "A"; }
    }

    public string B
    {
      get{ return "B"; }
    }

    public int C
    {
      get{ return 1; }
    }
  }
}
Jeff Mastry Send private email
Wednesday, May 04, 2005
 
 
Jeff,
Thanks for the code sample. So my idea is implementable using your code -- one hurdle passed for me. Do you have any idea of the performance hit using this? Would it be significant/noticeable?
Different Approach Send private email
Wednesday, May 04, 2005
 
 
I remember reading something that indicated that reflection causes an extremely large performance penalty.  I think in 1.0 it was something crazy like normal code read 400 times faster than reflection code.  That said, something that doesn't happen with extreme frequency will still run fast enough, and if it's something that's happening from direct response to a user action, they will most likely already be in the mindset of waiting for a reply, so taking 2 seconds instead of .05 (for example) won't really make a difference unless you have a large number of users and they are all using the application off of the same server.

What database are you using?  I know SQL Server allows you to specify default values for fields, as well as marking them as non-nullable.  If you're going to use code that throws exceptions, the performance penalty of receiving an SqlException when trying to update a field to NULL won't be any worse, but it won't have the reflection overhead.  I haven't had the opportunity to work with any other flavors of database engines, so am not sure whether default and non-null are constraint types that are common.

If you're using stored procedures, you could also implement the default values there.  If you're using dynamic sql for everything, the function you have to build the sql statement could also replace NULLs with the default values.  The drawback there is that you then have to either recompile or use some type of data/ini file to change the default values.
Neyah
Wednesday, May 04, 2005
 
 
Also, you didn't explicitely mention whether you meant your strings couldn't be null as in "object o = null" or null as in "object o = DBNull.Value" 

If it's the former, you should have that check in your wrapper's property setter for each string field.

private string _f1;
public string f1
{
  get { return _f1;}
  set
  {
    if (value == null)
      throw new ArgumentNullException(value);
    else
      _f1 = value;
  }
}

After thinking about it a little more, I would probably go with having the default values assigned by the constructor for your wrapper, along with checking for nulls in the property setters.  Then it would work no matter what database you ended up using for your back end, and if you ever end up needing to internationalize the strings, you can have your constructor obtain the default values from a resource file, rather than needing a different compile for each language.
Neyah
Wednesday, May 04, 2005
 
 
Neyah is correct in stating that there is some overhead involved with reflection. However, I just use it when I need it and worry aboout performance later (if I have to - which is actually rare). I typically write services so the network lag is much more significant than the reflection lag.
Jeff Mastry Send private email
Wednesday, May 04, 2005
 
 
Neyah,

I'm using SQL server. I don't want to go all the way to the database to find out that a field was null and handle the exception. I don't want to use "defaults" either. These database wrapper objects are sort of the mid-tier ensuring data integrity. The pain in the way it has to be implemented in a straight-fwd code is really the mind numbing repetitiveness that's prone to cut-paste errors and review oversight.

With reflections, I could have a base rule
for e.g. string[] fields = new string[] {"name,1","address,1","phone,1","fax,0"};

and the reflection would compare property of the object with the array (using a split etc.) and if the value is 1 then if null return fail, if 0 treat as optional etc. I could extend it to a data driven approach by having the compare method in the wrapper base class and define the arrays within the implementation. I see that reducing quite a bit of code - unless I'm missing something here. The flip side is the performance issue - but if this specific thing happens only once a while, it shouldn't be a big deal.

Any opinions? Jeff?
Different Approach Send private email
Thursday, May 05, 2005
 
 
If you go the reflection route, I would suggest you look into  defining a custom attribute which you would then use to "decorate" applicable properties, rather than dealing with some array to track which properties you care about. Something like this:

[DisallowNulls]
public string SomeNonNullableField
{
    get {...}
    set {...}
}

While you are reflecting your object, you can test the properties to see if they have had the "DisallowNulls" attribute applied to them, and if so do your null check. It's a bit more elegant than separately maintaining an array. If for some reason you don't want to use an attribute, at least use something like a StringCollection for tracking *only* the properties which should disallow nulls. Doing so lets you do a simple contains check against the StringCollection, rather than having to iterate over every item in your string[] for each property, split the item and finally check for a 0 or 1. The overhead of that unnecessary processing would start to rival the overhead of using reflection in the first place.

And finally...don't use reflection for this. While reflection is seriously powerful and usefull in certain situations, using it to avoid repetative, boring code entry is not one of them. Just use a code generator, such as the excellent CodeSmith - http://www.ericjsmith.net/codesmith/ - which will let you define a template which, when executed, will connect to your db and auto-generate a class for any/all tables, including null checks for table fields which disallow them. Write one template, generate an unlimited number of classes. There are many, many templates already created for CodeSmith, so you wouldn't even have to start from scratch. Oh...and it's free.

Good luck.
Ryan LaNeve Send private email
Thursday, May 05, 2005
 
 
Ryan,
Thanks! I guess I'll look at codesmith. Your points make a lot of sense. It's just that I get upset when I see lots of "similar looking code" :) but I kept myself from going any route until I got other opinions.
v Send private email
Friday, May 06, 2005
 
 
Ryan makes some great points - I highly recommend using the custom attribute approach if you go with reflection.

If you go with a code generator, then just make sure that you can understand and maintain the code it generates.
Jeff Mastry Send private email
Friday, May 06, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz