Ben McCormack
Ben McCormack

Reputation: 33078

What ways can I ensure that a string property is of a particular length?

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.

Upvotes: 1

Views: 10134

Answers (5)

Wix
Wix

Reputation: 2214

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.

Upvotes: 1

kbrimington
kbrimington

Reputation: 25652

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.

Upvotes: 2

VdesmedT
VdesmedT

Reputation: 9113

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.

Upvotes: 1

James Curran
James Curran

Reputation: 103505

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.

Upvotes: 2

kbrimington
kbrimington

Reputation: 25652

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.

Upvotes: 0

Related Questions