views:

105

answers:

5

I've created some classes that will be used to provide data to stored procedures in my database. The varchar parameters in the stored procs have length specifications (e.g. varchar(6) and I'd like to validate the length of all string properties before passing them on to the stored procedures.

Is there a simple, declarative way to do this?


I have two conceptual ideas so far:

Attributes

public class MyDataClass
{
     [MaxStringLength = 50]
     public string CompanyName { get; set; }
}

I'm not sure what assemblies/namespaces I would need to use to implement this kind of declarative markup. I think this already exists, but I'm not sure where and if it's the best way to go.

Validation in Properties

public class MyDataClass
{
     private string _CompanyName;
     public string CompanyName
     {
         get {return _CompanyName;}
         set
         {
              if (value.Length > 50)
                  throw new InvalidOperationException();
              _CompanyName = value;
         }
     }
}

This seems like a lot of work and will really make my currently-simple classes look pretty ugly, but I suppose it will get the job done. It will also take a lot of copying and pasting to get this right.

+1  A: 

It sounds like a business rule. So I would put it in a Company class (Since it is CompanyName), and do the validation there. I don't see why it would require copying and pasting if you have it encapsulated.

Either an attribute or your second example should be fine. The attribute allows for reuse in other classes with string length constraints, however.

Wix
A: 

Though not exactly the same thing, I recently became aware of .NET 4 Code Contracts in an MSDN article. They provide a convenient and elegant way of encoding and analyzing code assumptions. It's worth taking a look at.

kbrimington
I had read a little about Code Contracts, but I wasn't able to quickly see how I could use them for my problem at hand. Any ideas?
Ben McCormack
I think something like `Contract.Requires(value == null || value.Length < 50)` would be the thing. It's not a perfect analog to what you are trying to accomplish, if I understand correctly, as contracts appear to exist for static checking, but can be removed in compilation.
kbrimington
+2  A: 

Well, whatever way you go, what's executed is going to look like your second method. So the trick is getting your first method to act your second.

First of all, It would need to be [MaxStringLength(50)]. Next, all that's doing is adding some data to the Type object for this class. You still need a way of putting that data to use.

One way would be a binary re-writer. After compilation (but before execution), the rewriter would read the assembly, looking for that Attribute, and when finding it, add in the code for the check. The retail product PostSharp was designed to do exactly that type of thing.

Alternately, you could trigger it at run-time. SOmething like:

public class MyDataClass 
{ 
     private string _CompanyName;

     [MaxStringLength(50)] 
     public string CompanyName 
     { 
         get {return _CompanyName;} 
         set 
         { 
             ProcessValidation()
              _CompanyName = value; 
         } 
     } 
}

That's still quite ugly, but it's a bit better if you have a number of validation attributes.

James Curran
The code you provide at least looks better than my code because the validation constraint is declared in the attribute. How would the `ProcessValidation` see the `MaxStringLength` attribute and know what property it is working with?
Ben McCormack
Reflectively. The ProcessValidation() method can know the type of your object (either this.GetType() or a similar call on a passed parameter), and from there it can get information for the member CompanyName and by extension the attributes decorating it. The attribute can be just a flag telling a central validator routine the exact rules to apply, or you can put the validation rule in the attribute and reflectively call some Evaluate() method on the attribute itself. Declarative validation can get messy, but that mess can be hidden behind the scenes unlike simple Validate() class members.
KeithS
+1  A: 

The first method using attribute sounds good.

Implement your attribute by inherit from the System.Attribute class and mark your class with AttributeUsage attribute to let your attribute being set on a field.

Then, using reflection, check for presence and value of the attribute before sending the value to the SP.

Thats provide you with lot more flexibility than the second method. If tomorow you decide to let your SP receive the first N chars of a too lengthly string, you won't have to modify all your code but only the one that interpret the attribute.

There are indeed some validation attribute in the framework but I wouldn't use those one because you could implies some behaviour you don't expect and because you won't be able to modify then in any way (liek if you want something like [MaxLength(50, true)] to specify that using the first 5O chars is OK.

VdesmedT
+1  A: 

I'll post this as a different answer, because it is characteristically different than Code Contracts.

One approach you can use to have declarative validation is to use a dictionary or hash table as the property store, and share a utility method to perform validation.

For example:

// Example attribute class for MaxStringLength
public class MaxStringLengthAttribute : Attribute
{
    public int MaxLength { get; set; }
    public MaxStringLengthAttribute(int length) { this.MaxLength = length; }
}

// Class using the dictionary store and shared validation routine.
public class MyDataClass
{
    private Hashtable properties = new Hashtable();

    public string CompanyName
    {
        get { return GetValue<string>("CompanyName"); }

        [MaxStringLength(50)]
        set { SetValue<string>("CompanyName", value); }
    }

    public TResult GetValue<TResult>(string key)
    {
        return (TResult)(properties[key] ?? default(TResult));
    }

    public void SetValue<TValue>(string key, TValue value)
    {
        // Example retrieving attribute:
        var attributes = new StackTrace()
                             .GetFrame(1)
                             .GetMethod()
                             .GetCustomAttributes(typeof(MaxStringLengthAttribute), true);
        // With the attribute in hand, perform validation here...

        properties[key] = value;
    }
}

You can get at the calling property using reflection by working up your stack trace as demonstrated here. Reflect the property attributes, run your validation, and voila! One-liner getter/setters that share a common validation routine.

On an aside, this pattern is also convenient because you can design a class to use alternative dictionary-like property stores, such as ViewState or Session (in ASP.NET), by updating only GetValue and SetValue.

One additional note is, should you use this approach, you might consider refactoring validation logic into a validation utility class for shared use among all your types. That should help prevent your data class from getting too bulky in the SetValue method.

kbrimington
Thanks for the example. I like how you show the use of reflection and custom `MaxStringLength` class. That should give me a good start. I'm not sure about using a hashtable for the properties, but that's not a bad idea.
Ben McCormack
@kbrim I added a follow-up question about the attributes here: [Use reflection to get attribute of a property via method called from the setter](http://stackoverflow.com/questions/3628630/use-reflection-to-get-attribute-of-a-property-via-method-called-from-the-setter)
Ben McCormack