advanced re-motion mixins -- removing items, scoping of mixin configurations

Mixin scopes are useful for mixing at run-time and limiting the "reach of action" for certain mixins. If nothing else, this is eminently useful for testing. This page explains how to construct and use mixin scopes. The Hotel mixin sample's Hotel.Tests project makes good use of transaction scopes for testing.

In the previous example, we have made an entirely home-grown configuration the active configuration for the entire thread. For the following example, we will introduce three modifications to that idea.

  1. We will derive our custom configuration from the existing default master configuration (which in turn is a reflection of all the Extends and Uses attributes in the application).
  2. Instead of adding to that configuration, we will remove from it with SuppressMixin<>.
  3. We will make the custom configuration active within a limited scope, i.e. temporarily.

Since the simplistic dessert wax sample does not lend itself very well to our sophisticated endeavor, we will resort to the slightly more advanced parrot on a unicycle here.

The following sample is based on the class hierarchy of parrots from the page

re-motion mixins basics -- what about sub-classes of the target class?

[ Parrot, GreyParrot and Macaw, with Parrot being the base class for GreyParrot and Macaw. ]

The entire hierarchy is extended by the CircusMixin which gives the entire hierarchy the ability to ride a unicycle. This configuration is read in and made the master configuration, which in turn is the active configuration for the given task.

Since we can't modify an existing configuration, we have to build a new one based upon the active configuration and make the active configuration for our thread.

As explained on page re-motion mixins basics -- what about sub-classes of the target class? the entire class hierarchy is extended by a mixin if that mixin is applied to the base class. In our example, we will exempt the Macaw class from being mixed. The new methods in the following snippet are

  • MixinConfiguration.BuildFromActive () – clones the active configuration
  • SupressMixin<> () – removes the specified mixin from the given class
var t = typeof (IMixinTarget);

var myMixinConfiguration = MixinConfiguration.BuildFromActive () // build upon active configuration
 .ForClass<Macaw> ().SuppressMixin<CircusMixin> ()               // remove the circus mixin from the 'Macaw' class
 .BuildConfiguration ();                                         // finish construction 

At this point we have a new mixin configuration in the myMixinConfiguration variable. It is exactly like the default master configuration that has been assembled from inspection of the executable. In the default master configuration, the CircusMixin is applied to Parrot, GreyParrot and Macaw. In our improved clone, the CircusMixin is only applied to Parrot and GreyParrot. Note that we haven't made the configuration active yet. Remember that we want to do it temporarily, not for the entire life-time of the thread. For this, we have to create a mixin configuration scope from myMixinConfiguration. You do this with the method EnterScope:

... some configuration is active here ...
using (myMixinConfiguration.EnterScope ())
{
  ... code code code ...
  ... where 'myMixinConfiguration' is active ...
} // end of scope

... previous mixin configuration is active again ...

The using spans the scope for which the configuration is active. As soon as execution of the code passes the closing bracket } of the using, the cloned configuration vanishes and the previous configuration becomes active again. (People who know the PhoneBook tutorial will recognize this mechanism: this is how re-stores transaction scopes work.)

You can even nest such scopes to arbitrary depth:

... configuration A is active here ...
using (mixinConfiguration_B.EnterScope ())
{
  ... code code code ...
  ... where 'mixinConfiguration_B' is active ...

  using (mixinConfiguration_C.EnterScope ())
  {
    ... code code code ...
    where 'mixinConfiguration_C' is active ...
  } // end of scope for mixinConfiguration_C
  ... mixinConfiguration_C is active again ...

} // end of scope for mixinConfiguration_B

The following code sample utilizes using for our special configuration that has mixing for Macaw suppressed.

  class Program
  {
    static void Main (string[] args)
    {
      // mind the loading of 'Remotion.dll'!
      var t = typeof (IMixinTarget);

      var myMixinConfiguration = MixinConfiguration.BuildFromActive ()   // clone the currently active configuration 
                                                                         // (that happens to be the master default config 
                                                                         // at this point)
        .ForClass<Macaw> ().SuppressMixin<CircusMixin> ()                // remove the mixing of the circus mixin from the 'Macaw' class
        .BuildConfiguration ();                                          // cause the actual building of our clonee 

      // now we span a scope for our special configuration
      using (myMixinConfiguration.EnterScope ())
      {
        // The mixed parrot class works as usual, no suppression here
        var myCircusParrot = ObjectFactory.Create<Parrot> (ParamList.Empty);
        Console.WriteLine ("Parrot:");
        myCircusParrot.Whistle();
        ((ICircusMixin) myCircusParrot).RideUniCycle();
        Console.WriteLine();

        // The mixed grey parrot class works as usua, no suppression here either
        Console.WriteLine ("GreyParrot:");
        var myCircusGreyParrot = ObjectFactory.Create<GreyParrot> (ParamList.Empty);
        myCircusGreyParrot.DestroyFurniture();
        ((ICircusMixin) myCircusGreyParrot).RideUniCycle();
        Console.WriteLine();

        // The macaw can do as macaws do (crack nuts)...
        Console.WriteLine ("Macaw:");
        var myBogusCircusMacaw = ObjectFactory.Create<Macaw> (ParamList.Empty);
        myBogusCircusMacaw.CrackNut();
        try
        {
          // ... but it can't ride the unicycle, because that's been
          // suppressed
          ((ICircusMixin) myBogusCircusMacaw).RideUniCycle ();
        }
        catch (InvalidCastException)
        {
          // ... what gives us a bummer at run-time
          Console.WriteLine ("... or not. We caught an 'InvalidCastException'.");
          Console.WriteLine ("The 'Macaw' class can't be cast to 'ICircusMixin'");
          Console.WriteLine ("because we suppressed that mixin. (Remember?)");
        }
        
        Console.WriteLine();
      } // end of scope with the non-unicycle macaw

      // at this point, the original (default master) configuration
      // is active again, so the macaw CAN ride the unicycle
      Console.WriteLine ("Macaw:");
      var myWorkingCircusMacaw = ObjectFactory.Create<Macaw> (ParamList.Empty);
      myWorkingCircusMacaw.CrackNut ();
      ((ICircusMixin) myWorkingCircusMacaw).RideUniCycle ();
      Console.WriteLine ();

      Console.ReadLine ();
    }
  }

If you need the new configuration only once, you can directly call EnterScope on the builder without using BuildConfiguration. BuildConfiguration is useful when you need to store the configuration in a variable in order to use it more than once.

Sample code

You find the complete sample code for this exercise in subversion

A more elaborate, and common, application of mixin scopes can be found in the unit-tests of the Hotel mixin sample, discussed there.