天天看点

How to use Microsoft Unity to implement Ioc and AOP mechanism

    Up to now Inversion of control and interception were very popular in software design, because of using they can reduce dependency between different modules. If we use Ioc or Interception mechanism excessively to design software, it will increase coding complexity. So if you want to use them to resolve some problems, you should investigate is there another simple way can also do it. In this post I will give you guys a simple work through that how to use Microsoft Unity. :)

    Below is a class diagram describing the structure of the demo:

How to use Microsoft Unity to implement Ioc and AOP mechanism

     The implementation of Ioc mechanism in Microsoft Unity include three aspects: Constructor Injection, Property Injection, Method Injection. Their detail information as following:

       Constructor Injection: Class' constructor method will be invoked when using Unity's API to get an instance of the class.

       Property Injection: As same as above, related property will be invoked.

       Method Injection: As same as above.

   To test above Injections we should set the configuration file to switch on injection function. For the detail please look at below code snippet:

public class SuperBomb:MarshalByRefObject
    {
        private IPowder powder;
        private ILeaded leaded;
        private ICoat coat;

        public SuperBomb(IPowder powder) {
            this.powder = powder;
        }

        [Dependency]
        public ILeaded BombLeaded {
            get { return leaded; }
            set { leaded = value; }
        }

        [InjectionMethod]
        public void MakeBombCoat(ICoat coat)
        {
            this.coat = coat;
        }

        
        public void Fire()
        {
            Console.WriteLine("Bomb!");
        }

        public override string ToString()
        {
            return string.Format("SuperBomb's description:\n{0}\n{1}\n{2}", powder.GetDetail(), coat.GetDetail(), leaded.GetDetail());
        }
    }
           

Configuration File

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <containers>
            <container name="defaultContainer">
                <register type="UnityDemo1.IPowder, UnityDemo1" mapTo="UnityDemo1.BombPowder, UnityDemo1"/>
                 <register type="UnityDemo1.ILeaded, UnityDemo1" mapTo="UnityDemo1.BombLeaded, UnityDemo1"/>
                 <register type="UnityDemo1.ICoat, UnityDemo1" mapTo="UnityDemo1.GoldCoat, UnityDemo1"/>
              
            </container>      
    </containers>    
  </unity>
  <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>
           

Invoker

IUnityContainer container = new UnityContainer();
            UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
            configuration.Configure(container, "defaultContainer");
            
            SuperBomb bomb2 = container.Resolve<SuperBomb>();
            Console.WriteLine(bomb2.ToString());
           

     The result is that all properties of the instance of the SuperBomb were set automatically. In the case we used all of Unity's injection mechanisms to instantiate SuperBomb: using Constructor Injection to set field power's value, using  Property Injection to set field leaded's value, using method injection to set field coat's value.  The instantiating flow as below:

    1. Using configuration file to create some necessary instances which were passed into function as input parameter, Such as BombPowder, BombLeaded,GoldCoat.

    2. Using SuperBomb's constructor to instantiate an instance, all of parameters constructor needs will be created in step 1.

    3. If there are some properties were flagged Dependency attribute, then set its value using related parameter created in step 1.

    4. If there are some mehods were flagged Injection Method, then call them and pass related parameter created in step 1.

   Now Aspect Oriented Programming  is a popular concept in software design, because it can reduce the amount of coding. Why? For example if we want to cache a query result, commonly we will implement a function to hold query result in cache. And application will return result in cache if it existed. Once the cache function was created, then we will use it in any place where need to cache result. The common resolving way is that modify all of existed code to reference the cache function. If the existed project is so large, it's impossible to modify all of code. To resolve the situation, we need a silver bullet that AOP.

   There are two ways can be used to implement AOP in Microsoft Unity. One is using interception behavior that it can intercept destination method to add some custom processes, the another is using interception attribute that if one method was flagged by the type attribute, the attribute add also some custom processes before invoking the actual process. Below is a simple demo about interception mechanism:

Interception Behavior

class PlaceBombBehavior:IInterceptionBehavior
    {
        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            Console.WriteLine("Start to place bomb by behavior!");
            return getNext()(input, getNext);
        }

        public bool WillExecute
        {
            get { return true; }
        }
    }
           

Configuration File

<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
    <alias alias="PlaceBombBehavior" type="UnityDemo1.PlaceBombBehavior,UnityDemo1"></alias>
    <containers>
            <container name="defaultContainer">
                <extension type="Interception"/>
              
                <register type="UnityDemo1.IPowder, UnityDemo1" mapTo="UnityDemo1.BombPowder, UnityDemo1"/>
                 <register type="UnityDemo1.ILeaded, UnityDemo1" mapTo="UnityDemo1.BombLeaded, UnityDemo1"/>
                 <register type="UnityDemo1.ICoat, UnityDemo1" mapTo="UnityDemo1.GoldCoat, UnityDemo1"/>
              <register type="UnityDemo1.SuperBomb, UnityDemo1" mapTo="UnityDemo1.SuperBomb, UnityDemo1">
                <interceptor type="TransparentProxyInterceptor"/>
                <interceptionBehavior name="Behavior1" type="PlaceBombBehavior" />
                <policyInjection />
              </register>
              
            </container>      
    </containers>    
  </unity>
           

Invoker

IUnityContainer container = new UnityContainer();
            UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
            configuration.Configure(container, "defaultContainer");
            container.AddNewExtension<Interception>();
            
            SuperBomb bomb2 = container.Resolve<SuperBomb>();

            Console.WriteLine(bomb2.ToString());
           

   Using the way we do not modify existed codes, because the interception process was map into configuration file. :)

Interception Behavior

class PlaceBombHandler:ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine("Start to place bomb!");
            Console.WriteLine("End to place bomb!");
            return getNext()(input, getNext);
        }

        public int Order
        {
            get;
            set;
        }
        
    }
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Method)]
    class PlaceBombAttribute:HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new PlaceBombHandler() { Order=this.Order};
        }
    }
           

 Configuration File

<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
    <alias alias="PlaceBombBehavior" type="UnityDemo1.PlaceBombBehavior,UnityDemo1"></alias>
    <containers>
            <container name="defaultContainer">
                <extension type="Interception"/>
              
                <register type="UnityDemo1.IPowder, UnityDemo1" mapTo="UnityDemo1.BombPowder, UnityDemo1"/>
                 <register type="UnityDemo1.ILeaded, UnityDemo1" mapTo="UnityDemo1.BombLeaded, UnityDemo1"/>
                 <register type="UnityDemo1.ICoat, UnityDemo1" mapTo="UnityDemo1.GoldCoat, UnityDemo1"/>
              <register type="UnityDemo1.SuperBomb, UnityDemo1" mapTo="UnityDemo1.SuperBomb, UnityDemo1">
                <interceptor type="TransparentProxyInterceptor"/>
                <interceptionBehavior name="Behavior1" type="PlaceBombBehavior" />
                <policyInjection />
              </register>
              
            </container>      
    </containers>    
  </unity>
           

 Flagged class SuperBomb

[PlaceBomb]
    public class SuperBomb:MarshalByRefObject
    {
        private IPowder powder;
        private ILeaded leaded;
        private ICoat coat;

        public SuperBomb(IPowder powder) {
            this.powder = powder;
        }
}
           

Summary

    As mentioned in the post, both of Ioc and AOP are popular in software design. But we should avoid to overdesign using them, because it will also increase your code complexity.

 Source Code