On the drive home tonight I listened to this episode of Ron Jacobs' ARCast. In it, he talked with Denny Boynton about SOA, and some of the best- and worst-practices ("patterns and anti-patterns") associated with creating services.

One of the topics that came up is something I've thought a lot about in the past - how to tell the service what kind of details you want in the objects you're requesting.

For example, let's say you're calling off to a service to retrieve a list of customers. What do you need in that list? Obviously each customer should have a name; possibly an address; but do you really need their list of contacts, or outstanding invoices? If you're expecting to get a list containing 1000 customers, then no - you only want the bare-minimum of details back.

Ron suggested that in the real, "on-paper", world, you'd probably submit a "customer list request" form, and that form would have a series of checkboxes letting you specify the details you need. If you translate that idea to code, I guess you could define an enum which has a value for each property in your Customer class:

[Flags] 
public enum CustomerProperties { Name, Address, Contacts, Invoices } 

So now you could pass that to the service and it could populate the list of Customer objects accordingly.

This, to me, seems a bit too tightly-coupled. I was thinking about a way you could do this so that the calling program didn't have to worry too much about individual properties.

The idea I've come up with is a way to specify a "detail level" using custom attributes.

First, we define a new DetailLevelAttribute class:

[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)] 
class DetailLevelAttribute : Attribute 
{ 
    int level = 0; 

    public DetailLevelAttribute(int level) 
    { 
        this.level = level; 
    } 

    public int Level 
    { 
        get { return level; } 
    } 
} 

Now we can apply that attribute to all the properties in our class:

class Customer 
{ 
    [DetailLevel(0)] 
    public string Name 
    { 
        get; set; 
    } 

    [DetailLevel(0)] 
    public string Address 
    { 
        get; set; 
    } 

    [DetailLevel(1)]
    public List<Invoice> Invoices { get { return _invoices; } } 
} 

Ok! So now we have a Customer class whose properties are each decorated with a DetailLevel attribute. Our service, then, can accept a "requiredDetailLevel" parameter, and using reflection, decide whether the property needs to be populated before returning the customer:

int requiredDetailLevel = 1;
Type custType = typeof(Customer); 
Customer cust = new Customer(); 
if (IsPropertyRequired(custType, "Name", requiredDetailLevel)) 
{ 
    // populate cust.Name 
} 

if (IsPropertyRequired(custType, "Address", requiredDetailLevel)) 
{ 
    // populate cust.Address 
} 

if (IsPropertyRequired(custType, "Invoices", requiredDetailLevel)) 
{ 
    // populate cust.Invoices 
} 

... which just leaves us to define the "IsPropertyRequired" method:

bool IsPropertyRequired(Type type, string propName, int maxDetail) 
{ 
    Type dlaType = typeof(DetailLevelAttribute); 

    System.Reflection.PropertyInfo prop = type.GetProperty(propName); 
    if (prop == null) 
    { 
        // Property not found. Maybe raise an exception. 
        return false; 
    } 

    object[] attrs = prop.GetCustomAttributes(dlaType, false); 
    if (attrs.Length == 0) 
    { 
        // Not decorated with DetailLevelAttribute. Assume required. 
        return true; 
    } 

    DetailLevelAttribute dla = attrs[0] as DetailLevelAttribute; 
    if (dla == null) 
    { 
        // Something's gone bananas. Maybe Assert. 
        return true; 
    } 

    return dla.Level <= maxDetail; 
} 

So now our service has a way to determine whether a property needs to be filled out, and it doesn't need to know too much about the object itself. The developer who designs the objects gets to specify which bits belong to which "detail level" using attributes.

Could this work? I dunno. But I thought it was worth documenting - let me know your thoughts!