Attribute Surrogate

I’m building an application server that is heavily based on attributes, DI (indirectly via an IOC), and the command pattern.

Basically, you can tell it which “features” to start. It finds those features via attributes (by name), then starts all command processors associated to the feature (also defined via attributes). Each things announces the other things it needs, then those things are found and started.

When you pull an attribute off of a type, you are given an instance of a class. It’s a regular class for which you can use the default constructor, or a constructor that accepts primitive types.

In some cases in the app server, I want the attribute to represent an object that is populated by the DI container. How do you get the IOC (or some other factory) to construct an instance of an attributes?

As far as I know, you can’t. So, I’m using what I am calling an attribute surrogate.

The attribute surrogate is an attribute that refers to a class. The thing consuming the attribute extracts the type information from the attribute, then uses the IOC to construct the other type.

The surrogate attribute:

   1: namespace SomeGuySoftware.ApplicationServer.Contracts

   2: {

   3:     using System;

   4:     using SomeGuySoftware.Common;

   5:  

   6:     [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]

   7:     public class CommandProviderSurrogateAttribute : Attribute

   8:     {

   9:         public CommandProviderSurrogateAttribute(string name)

  10:         {

  11:             Validators.CheckRequiredArgument("name", name);

  12:             this.Name = name;

  13:         }

  14:  

  15:         public string Name { get; private set; }

  16:     }

  17: }

You see that in this case it has a NAME property that is a string. I started by using a TYPE, which works just as well, but in the case of the app server it’s using the string as everything else within it does. (For more generic purpose, change the NAME attribute to a TYPE).

I slap that attribute onto a class:

   1: namespace SomeGuySoftware.ApplicationServer.Contracts

   2: {

   3:     using System;

   4:  

   5:     /// <summary>

   6:     /// Put this on a feature to register a Wcf service.

   7:     /// </summary>

   8:     [CommandProviderSurrogate("WcfCommandProvider")]

   9:     [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]

  10:     [DependsOnFeature("WcfHostFeature")]

  11:     public class HostWcfServiceAttribute : WcfAttribute

  12:     {

  13:         /// <summary>

  14:         /// </summary>

  15:         /// <param name="name">The name of the service to register.</param>

  16:         public HostWcfServiceAttribute(string name)

  17:             : base(name)

  18:         {

  19:         }

  20:     }

  21: }

Now, the app server sees the Command Provider Surrogate attribute and pulls off the name property. It uses the Type Cache (previously blogged about) to find the type by name. (The actual type has a NAME attribute on it which is used to identify and find it). Then, the Type is passed to the IOC which constructs an instance of that type. The IOC does all of the dependency injection, etc, just like it’s supposed to.

Here is the method that does all of that work. IObjectFactory is an abstraction on top of the IOC. As far as the code is concerned, it’s using a generic factory. The implementation of it is an unity container.

   1: private void AddCommandsFromSurrogateAttribute(Attribute attribute)

   2: {

   3:     /*

   4:      The attribute may be a surrogate - this means that the class is decorated with the generic [CommandProviderSurrogate].

   5:      The better practice is to decorate a user-friendly attribute with [CommandProviderSurrogate].

   6:      The user-friendly attribute has properties that can be consumed by the surrogate command provider.

   7:      Example: [HostWcfServiceAttribute] is decorated by [CommandProviderSurrogate].

   8:      HostWcfServiceAttribute is a specific surrogate to start a service.*/

   9:  

  10:     // is the attribute a surrrogate?

  11:     var surrogateAttribute = attribute as CommandProviderSurrogateAttribute;

  12:     if (surrogateAttribute == null)

  13:     {

  14:         // does the attribute have a surrogate attribute?

  15:         surrogateAttribute = Attribute.GetCustomAttribute(attribute.GetType(), typeof(CommandProviderSurrogateAttribute)) as CommandProviderSurrogateAttribute;

  16:         if (surrogateAttribute == null)

  17:         {

  18:             return;

  19:         }

  20:     }

  21:  

  22:     var surrogateType = this.typeCache.GetType<ICommandProviderSurrogate>(surrogateAttribute.Name, false);

  23:     if (surrogateAttribute == null)

  24:     {

  25:         throw ApplicationServerExceptions.CommandProviderSurrogateIsntSurrogateType(attribute, this.feature);

  26:     }

  27:  

  28:     var surrogate = this.objectFactory.GetInstance<ICommandProviderSurrogate>(surrogateType);

  29:     surrogate.SourceFeature = this.feature;

  30:     surrogate.SourceAttribute = attribute;

  31:  

  32:     var commands = this.getter.Invoke(surrogate);

  33:     if (commands == null)

  34:     {

  35:         return;

  36:     }

  37:  

  38:     this.resultCommands.AddRange(commands);

  39: }

For more generic use, have the attribute specify a type, then you can eliminate the TypeCache part of it.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: