Blogs

Intelligent Design

Choppin' properties

C# Enumerations have always been a sore area for developers.  Binding (lack of), comparison, explicit conversion, etc...


Over all enumerations are good mechanisms to help you avoid hitting the database - for predefined system types.  So what happens if your type has some sort of metadata hanging off of it and you still want to use a static representation of the data? 


Let's say you have an entity named communication and it has a two properties hanging off of it:


public Enum.CommunicationType CommunicationType{get{}set{}}

public string CommunicationValue {get{}set{}} 

Enum.CommunicationType represents the following:

public enum CommunicationType{
     
HomePhone = 1,
   CellPhone = 2,
   Email = 3,
   AlternateEmail = 4,
   WebAddress = 5,
   IMAddress = 6
}


Now let’s say you want to validate the “CommunicationValue” in your business layer based on

CommunicationType, but CommunicationType does not tell us the intended context of value, just a label for the value.  So how do we tell the code to interpret values associated with communication types “HomePhone” or “CellPhone” that they should be validated as phone numbers?

Associate custom properties with the enum values and use reflection to access those properties when needed.  Here's how:


First make your custom property to be implemented by the enumeration (note: custom properties can be used by several different targets - reference the System.AttiributeTargets namespace for the list).

Something like:


[AttributeUsage(AttributeTargets.Enum)]

public class MetaData : System.Attribute {

      private string _value = null;

      public MetaData(string value) {

            _value = value;

      }

      public string Value {

            get {return _value;}

      }

}

So now only enumerations may implement our MetaData class.  We’re only allowing a get in this interface – if you wanted to you could overload the constructor passing in many data types and add properties to the class to extend functionality.  For instance, if you wanted to pass a regular expression as well as the “value” for validation during reflection you could do so by simply adding another interface and adding another property.


Next, we will use the MetaData class to help describe our Enumeration valueType.  For simplicity we will use strings to describe our data.


public enum CommunicationType{
   
[MetaData("PhoneNumber")]HomePhone = 1,
  [MetaData("PhoneNumber")]CellPhone = 2,
  [MetaData("Email")]Email = 3,
  [MetaData("Email")]AlternateEmail = 4,
  [MetaData("Web")]WebAddress = 5,
  [MetaData("Web")]IMAddress = 6
}

You can do this next part several different ways, we will use the GetField method hanging off of the Type:


CommunicationType ct = (CommunicationType)_communicationTypeID;

FieldInfo fi = ct.GetType().GetField(ct.ToString());

if (fi != null) {

MetaData[] attributes = (MetaData[])fi.GetCustomAttributes(typeof(MetaData), false);

      if (attributes.Length > 0) {

            if (attributes[0].Value != null) {

                  generalType = attributes[0].Value;

            }

      }

}


You can opt out of using the indexers on the attributes by iterating the attribute collection and finding your attribute if there were more than your specific implementation.


That’s it, dump the code in some BC util method or store it in your Communication entity as a helper method – passing in the enumeration instead of declaring it (like above) and you have instant attributes.  The reflection cost is very minimal since we’re dealing with either valueTypes or very simple referenceTypes.

Published Monday, May 21, 2007 12:30 PM by nfloyd
| Filed under: , , , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
(optional)
(required) 
Submit