Search code examples
c#dependency-injectionenterprise-library

TypeMatchingRule XML configuration resolves the class type instead of implemented interface


I am adapting the code found http://www.christophdebaene.com/blog/2008/11/02/aop-in-action-part-1-dirty-tracking-using-unity-interception/ to my own needs

I would do like to translate my working Unity fluent API adapted code section from Unity to Enterprise Library policy XML configuration

From this working fluent API…

   .AddNewExtension<Interception>()
   .RegisterInstance<ICallHandler>("DirtyCallHandler", new DirtyCallHandler(null))
   .Configure<Interception>()
            .AddPolicy("DirtyPolicy")
            .AddMatchingRule(new PropertyMatchingRule("*", PropertyMatchingOption.Set))
            .AddMatchingRule(new CustomAttributeMatchingRule(typeof(DirtyAttribute), true))
            .AddMatchingRule(new InstanceOfMatchingRule(typeof(IDirty)))
            .AddCallHandler("DirtyCallHandler")

To this XML configuration…

 <policies>
    <add name="DirtyPolicy">
      <matchingRules>
        <add name="SetPropertyRule" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.PropertyMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection">
          <matches>
            <add matchOption="Set" match="*" />
          </matches>
        </add>
        <add name="DirtyAttributeRule" attributeType="IoCnAOP.DirtyAttribute, IoCnAOP" searchInheritanceChain="true" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.CustomAttributeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection"/>
        <add name="InstanceOfIDirtyRule" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TypeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection">
          <matches>
            <add match=" IoCnAOP.IDirty" ignoreCase="false"/>
          </matches>
        </add>
      </matchingRules>
      <handlers>
        <add name="DirtyCallHandler" order="0" type="IoCnAOP.DirtyCallHandler, IoCnAOP" />
      </handlers>
    </add>
  </policies>

The problem is located at match="IoCnAOP.IDirty" that don’t match if the class implements the interface IDirty (However if I change it as follow match="Domain.Frog", I mean using the class type then it works, but this is not the requirement that I am looking for. I want to match if the class implements the given interface IDirty)

So… if my adapted the fluent API code works fine… but the XML configuration only resolves the class type.. something is missed on the XML configuration

How can I get it working using XML instead?

Thanks a lot in advance

BR

(so below some relevant code excerpts…)

    public interface IDirty
    {
        bool IsDirty { get; set; }
    }

    public class Frog :  IDirty
    {
        public bool IsDirty { get; set; }

        public virtual string JustAProperty { get; [Dirty]set; }
    }

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class DirtyAttribute : Attribute
    {
    }


       public class InstanceOfMatchingRule : IMatchingRule
        {
            private readonly Type _type;

            public InstanceOfMatchingRule(Type type)
            {
                _type = type;
            }

            public bool Matches(System.Reflection.MethodBase member)
            {
                return _type.IsAssignableFrom(member.DeclaringType);
            }
        }

[ConfigurationElementType(typeof(CustomCallHandlerData))]
public class DirtyCallHandler : ICallHandler
{
    public DirtyCallHandler(NameValueCollection attributes)
    {
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        if (getNext == null)
            throw new ArgumentNullException("getNext");

        Console.WriteLine("DirtyCallHandle Invoking {0} at {1}", input.MethodBase.Name, DateTime.Now.ToLongTimeString());
        IsDirty(input.Target, input.MethodBase, input.Arguments[0]);
        return getNext()(input, getNext);
    }

    public int Order { get; set; }

    private static void IsDirty(object target, MethodBase methodBase, object value)  //http://www.christophdebaene.com/blog/2008/11/02/aop-in-action-part-1-dirty-tracking-using-unity-interception/
    {
        if (((IDirty)target).IsDirty)
            return;

        var propertyName = methodBase.Name.Substring("set_".Length);
        var info = target.GetType().GetProperty(propertyName);

        if (info == null)
            return;

        var oldValue = info.GetValue(target, null);

        if (!IsEqual(value, oldValue))
            ((IDirty)target).IsDirty = true;
    }

    private static bool IsEqual(object valueX, object valueY)
    {
        if (valueX == null && valueY != null)
            return false;

        if (valueX != null && valueY == null)
            return false;

        return valueX == null || valueX.Equals(valueY);
    }
}

    <?xml version="1.0" encoding="utf-8" ?>
    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
      <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
      <container>
        <register type="Domain.Frog, Domain">
          <interceptor type="VirtualMethodInterceptor" />
          <policyInjection />
        </register>
      </container>
    </unity>

Solution

  • The configuration is using Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TypeMatchingRule which matches directly on the type and and not your custom InstanceOfMatchingRule.

    The easiest way to expose InstanceOfMatchingRule via XML configuration would be use the built-in CustomMatchingRuleData:

        [ConfigurationElementType(typeof(CustomMatchingRuleData))]
        public class InstanceOfMatchingRule : IMatchingRule
        {
            private readonly Type _type;
    
            public InstanceOfMatchingRule(NameValueCollection configuration)
            {
                _type = Type.GetType(configuration["targetType"]);
            }
    
            public InstanceOfMatchingRule(Type targetType)
            {
                _type = targetType;
            }
    
            public bool Matches(System.Reflection.MethodBase member)
            {
                return _type.IsAssignableFrom(member.DeclaringType);
            }
        }
    

    And then use the configuration:

    <policyInjection>
        <policies>
            <add name="DirtyPolicy">
                <matchingRules>
                    <add targetType="IoCnAOP.IDirty" type="IoCnAOP.InstanceOfMatchingRule, IoCnAOP, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                        name="InstanceOfIDirtyRule" />
                </matchingRules>
                <handlers>
                    <add name="DirtyCallHandler" order="0" type="IoCnAOP.DirtyCallHandler, IoCnAOP" />
                </handlers>
            </add>
        </policies>
    </policyInjection>
    

    to pass in the target type via the NameValueCollection. For programmatic use use the Type constructor.