Chapter 3. Attribute Reference

In this chapter, we will explore the attributes that are the essential components for declaring Object Definitions with Spring CodeConfig. These attributes have a corresponding representation in Spring XML so should be very familiar to current Spring.NET users.

3.1. [Configuration] Attribute

The [Configuration] attribute is applied to classes that contain one or more [Definition] attributed-methods. During scanning by the CodeConfigApplicationContext, only types having the [Configuration] attribute will be considered candidates to contain Object Definition configurations.

3.1.1. Using the Configuration Attribute

3.1.1.1. Simple Usage

The most common usage of the [Configuration] attribute simply applies the attribute to a class without any attribute parameters. The name of the of the type, once registered with the CodeConfigApplicationContext, will be the name of the class itself. Note that it is not a common use-case to ask the container for this configuration class.

[Configuration]
public class MyConfigurationClass
{
  //[Definition] methods here
}

3.1.1.2. Controlling the Name of the Configuration Class

While not a common use-case, if you need fine-grained control over the name of the type registered with the CodeConfigApplicationContext, the [Configuration] attribute accepts a Name parameter which will be applied to the registered Object Definition in the CodeConfigApplicationContext.

[Configuration("MySpecialConfigurationClass")]
public class MyConfigurationClass
{
  //[Definition] methods here
}

3.2. [Definition] Attribute

The [Definition] attribute is applied to one or more methods within any class to which the [Configuration] attribute has been applied. During scanning, any public virtual method having the [Definition] attribute is considered to contain Object Definition metadata.

3.2.1. Using the Definition Attribute

3.2.1.1. Simple Usage

The first usage of the [Definition] attribute simply applies the attribute to the method. The name of the of the Object to be registered with the CodeConfigApplicationContext, will be the name of the method itself.

[Configuration]
public class MyConfigurationClass
{
  [Definition]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

3.2.1.2. Controlling the Name and Aliases of the Defined Object

If you need to control the name of the Object registered with the CodeConfigApplicationContext, the [Definition] attribute accepts one or more comma-delimited names as aliases for the Object when registered.

[Configuration]
public class MyConfigurationClass
{
  [Definition(Names="TheHomeController")]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }

  [Definition(Names="TheSpecialNameForAboutController,AliasForAboutController")]
  public virtual AboutController AboutController()
  {
    return new AboutController();
  }
}

3.2.1.3. Setting an Init Method for the Object

If you need to declare an Initialization Method for the object definition, the [Definition] attribute accepts a method name to be invoked at the appropriate stage in the object's creation by the CodeConfigApplicationContext.

[Configuration]
public class MyConfigurationClass 
{
  [Definition(InitMethod="AfterCreation")] //assumes a public method on the HomeController type named 'AfterCreation()'
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}
[Note]Note

You can just also call the method 'AfterCreation' inside the body of the HomeController implementation itself.

3.2.1.4. Setting a Destroy Method for the Object

If you need to declare a Destroy Method for the object definition, the [Definition] attribute accepts a method name to be invoked at the appropriate stage in the object's destruction by the CodeConfigApplicationContext.

[Configuration]
public class MyConfigurationClass
{
  [Definition(DestroyMethod="CleanupResources")] //assumes a public method on the HomeController type named 'CleanupResources()'
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

3.3. [DependsOn] Attribute

The [DependsOn] attribute can be applied to any [Definition]-attributed method to declare a construction sequence dependency upon one or more objects that the CodeConfigApplicationContext will ensure are created prior to the creation of the current object. This is only required if there is a hidden depedency between the two classes that isn't exposed via constructor or setter properties.

3.3.1. Using the DependsOn Attribute

3.3.1.1. Controlling Creation Dependencies

The [DependsOn] attribute accepts one or more comma-delimited strings representing the names of the objects upon which the current object will depend.

[Configuration]
public class MyConfigurationClass
{
  [Definition]
  [DependsOn("Dependency1")] //HomeController requires "Dependency1" to be created first
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }

  [Definition]
  [DependsOn("Dependency1", "Dependency2")] //AboutController requires "Dependency1" and "Dependency2" to be created first
  public virtual AboutController AboutController()
  {
    return new AboutController();
  }b
}

3.4. [Import] Attribute

The [Import] attribute allows you to identify one or more additional types that will also be scanned when the current type is scanned.

3.4.1. Using the Import Attribute

3.4.1.1. Specifying Additional Types to Scan

To support your specifying additional types to scan, the [Import] attribute accepts one or more comman-delimited Types to scan as well. To be candidates for scanning, the types listed in this array must also have the [Configuration] attribute applied to them. Note that no error is reported if these types lack the[Configuration] attribute, but without it they will not satisfy the scanner's requirements for Configuration candidates.

[Configuration]
[Import(typeof(MySecondConfiguration), typeof(MyThirdConfiguration))]
public class MyConfigurationClass
{
  [Definition]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

[Configuration]
public class MySecondConfiguration
{
  [Definition]
  public virtual AboutController AboutController()
  {
    return new AboutController();
  }
}

[Configuration]
public class MyThirdConfiguration
{
  [Definition]
  public virtual SomeOtherController SomeOtherController()
  {
    return new SomeOtherController();
  }
}

3.4.1.2. Chaining [Import] Directives

Types pointed to by one [Import] attribute may in turn have their own [Import] attributes pointing to yet more types to scan. Using this approach, its possible to specify perhaps only a single 'root' class from which to 'begin' the scan and leverage the [Import] attribute to 'chain' successive types into the scanning scope, transitively pointing from one [Configuration]-attributed type to the next as in the following example where MyConfigurationClass has an [Import] attribute pointing to the MySecondConfiguration class which in turn has an [Import] attribute pointing to the MyThirdConfiguration class.

[Configuration]
[Import(typeof(MySecondConfiguration))]
public class MyConfigurationClass
{
  [Definition]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

[Configuration]
[Import(typeof(MyThirdConfiguration))]
public class MySecondConfiguration
{
  [Definition]
  public virtual AboutController AboutController()
  {
    return new AboutController();
  }
}

[Configuration]
public class MyThirdConfiguration
{
  [Definition]
  public virtual SomeOtherController SomeOtherController()
  {
    return new SomeOtherController();
  }
}

Given this series of transitive or 'chained' [Import]attributes, the CodeConfigApplicationContext would only need to be told to scan the single MyConfigurationClass type in order to effectively scan and discover the [Definition] methods contained all three [Configuration] types. Using this approach, its possible to use a compositional approach to segregate [Definition] methods into multiple [Configuration] classes and then chain them together just as one might do with XML based configuration files for many of the other IApplicationContext implementations.

3.5. [ImportResource] Attribute

Just as the [Import] attribute provides the ability to reference and import additional types attributed with the [Configuration] attribute, the [ImportResrource] attribute permits referencing and importing Object Defintions from any IResource implementation including those defined natively in Spring.NET ("file://", "assembly://", etc.).

3.5.1. Using the ImportResourceAttribute

3.5.1.1. Specifying an IResource

To import an IResource, simply reference its path in the [ImportResource] attribute. In the following example, the embedded assembly resource ObjectDefinitions.xml is being imported into the process of scanning and parsing the MyConfigurationClass type. This makes all of the object definitions present in the embedded ObjectDefinitions.xml file available to the CodeConfigApplicationContext as it builds its Object Defintions.

[Configuration]
[ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.xml")]
public class MyConfigurationClass
{
  [Definition]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

3.5.1.2. Specifying Multiple IResources

To import multiple IResources, simply provide multiple [ImportResource] attributes, each with the single resource to import. The following example demonstrates importing an embedded assembly resource as well as two XML files on disk.

[Configuration]
[ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.xml")]
[ImportResource("file://ServiceObjectDefinitions.xml")]
[ImportResource("file://c:/MySpecialConfigLocation/Deployment/SiteB/RepositoryObjectDefinitions.xml")]
public class MyConfigurationClass
{
  [Definition]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

3.5.1.3. Specifying a specific IObjectDefintionReader to Parse the IResource

By default, the [ImportResource] attribute will use the Spring.NET provided XmlObjectDefinitionReader to parse the imported IResource. If your imported resource cannot be parsed with the XmlObjectDefinitionReader then you can provide the type of the specific implementation of IObjectDefinitionReader that the [ImportResource] process should use. Note that this provided type must implement the IObjectDefinitionReader interface or an Exception will be thrown when the [ImportResource] attribute is evaluated.

[Configuration]
[ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.CSV", DefinitionReader = typeof(MyCommaSeparatedValueObjectDefinitionReader))]
public class MyConfigurationClass
{
  [Definition]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

3.6. [Lazy] Attribute

The [Lazy] attribute allows you to specify that the object described by the [Definition] should either be be lazily or eagerly instantiated.

3.6.1. Using the Lazy Attribute

3.6.1.1. Specifying Lazy Instantiation

To specify Lazy Instantiation of any singleton object by the CodeConfigApplicationContext, apply the [Lazy] attribute to any [Definition]-attributed method as in the following example. Note that the default usage of the[Lazy] attribute sets Lazy = true and so [Lazy] and [Lazy(true)] are considered functionally equivalent. Also note that specification of lazy instantiation is only valid for Singleton-scoped objects; any attempt to apply the [Lazy] attribute to a non-singleton-scoped [Definition] method will result in an exception thrown by the underlying ApplicationContext.

[Configuration]
public class MyConfigurationClass
{
  [Definition]
  [Lazy] //functionally equivalent to [Lazy(true)]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }

  [Definition]
  [Lazy] //invalid here because the scope is non-Singleton
  [Scope(ObjectScope.Prototype)]
  public virtual InvalidController InvalidController()
  {
    return new InvalidController();
  }
}

3.6.1.2. Specifying Non-Lazy (eager) Instantiation

As the default instantiation behavior for the CodeConfigApplicationContext is to perform eager instantiation, no special attribute needs to be applied to achieve eager instantiation of the object. However, the [Lazy] attribute will accept a bool value of false if you desire to be explicit about the [Lazy] setting for the [Definition] as shown in the following code snippet.

[Configuration]
public class MyConfigurationClass
{
  [Definition]
  [Lazy(false)] //functionally equivalent to simply not applying the [Lazy] attribute at all
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }
}

3.7. [Scope] Attribute

The [Scope] attribute permits you to declare the lifetime of the object managed by the CodeConfigApplicationContext for each [Definition]-attributed method.

3.7.1. Scope Attribute Usage

The [Scope] attribute accepts a single ObjectScope enum argument as defined by Spring.NET and demonstrated in the following snippet:

[Configuration]
public class MyConfigurationClass
{
  [Definition]
  [Scope(ObjectScope.Prototype)]
  public virtual HomeController HomeController()
  {
    return new HomeController();
  }

  [Definition]
  [Scope(ObjectScope.Singleton)] //technically redundant since all types default to Singleton ObjectScope
  public virtual CustomerRepository CustomerRepository()
  {
    return new CustomerRepository();
  }

  [Definition]
  [Scope(ObjectScope.Session)]
  public virtual UserSettings UserSettings()
  {
    return new UserSettings();
  }
}