Ent. Lib 5.0 Unity - Policy Injection (Ignore rules?)

May 24, 2012 at 10:06 AM
Edited May 24, 2012 at 1:34 PM

Hi,

I've implemented policy injection in a C# .NET module and it is working perfectly - intercepts methods I've defined in the matching rules as shown below:

 

<matchingRule name="MethodMatchingRule" type="MemberNameMatchingRule">
            <constructor>
              <param name="namesToMatch">
                <array type="string[]">
                  <value value="Get*"/>
                </array>
              </param>
            </constructor>
</matchingRule>

This work perfectly and intercepts all methods that start with "Get".

Now, I want to implement some ignore rules so that I can define some methods that the code should not intercept.  Can you please advise how this can be achieved through configuration or any other way?

Thanks a lot.

Cheers,

Yohan

May 24, 2012 at 4:57 PM

There are many built-in (non-attribute) matching rules that you could use to match on: assembly, member name, method signature, namespace, parameter type, property, type.

If you can't achieve the behavior you want by including various combinations of matching rules you could create another matching rule that excludes some member names:

    public class ExcludeMemberNameMatchingRule : IMatchingRule
    {
        private readonly List<Glob> patterns;

        public ExcludeMemberNameMatchingRule(string nameToExclude)
            : this(nameToExclude, false)
        {
        }

        public ExcludeMemberNameMatchingRule(string nameToExclude, bool ignoreCase)
        {
            patterns = new List<Glob>();
            patterns.Add(new Glob(nameToExclude, !ignoreCase));
        }

        public ExcludeMemberNameMatchingRule(IEnumerable<string> namesToExclude)
            : this(namesToExclude, false)
        {

        }

        public ExcludeMemberNameMatchingRule(IEnumerable<string> namesToExclude, bool ignoreCase)
        {
            patterns = new List<Glob>();
            foreach (string name in namesToExclude)
            {
                patterns.Add(new Glob(name, !ignoreCase));
            }
        }

        public ExcludeMemberNameMatchingRule(IEnumerable<MatchingInfo> exclusions)
        {
            patterns = new List<Glob>();
            foreach (MatchingInfo match in exclusions)
            {
                patterns.Add(new Glob(match.Match, !match.IgnoreCase));
            }
        }

        public bool Matches(MethodBase member)
        {
            bool shouldProcess = !patterns.Exists(pattern => pattern.IsMatch(member.Name));
            return shouldProcess;
        }
    }

Or you could create a RegEx member name matching rule that would let you specify a RegEx to match on member name.  This would probably support most scenarios except the RegEx would probably get a bit nasty if the rules were complicated (e.g. (?!^GetUserCreditCard|GetBankAccount$)(^Get.*$) ).

The config would look like this:

          <matchingRule name="rule1" type="MemberNameMatchingRule">
            <constructor>
              <param name="namesToMatch">
                <array type="string[]">
                  <value value="Get*" />
                </array>
              </param>
            </constructor>
          </matchingRule>
          <matchingRule name="rule2" type="ExcludeMemberNameMatchingRule">
            <constructor>
              <param name="namesToExclude">
                <array type="string[]">
                  <value value="GetUserCreditCard" />
                  <value value="GetBankAccount" />
                </array>
              </param>
            </constructor>
          </matchingRule>

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

May 24, 2012 at 5:05 PM
Edited May 24, 2012 at 5:05 PM

Hi Randy,

Thanks a lot for your quick reply.  This looks amazing - I'll give it a try and get back with the results.

Again thanks a lot for this detailed reply - saved my life.

 

Cheers,

Yohan

May 25, 2012 at 5:31 AM

Hi,

I've tried your suggestion and it looks like it is working.  However I think I'm missing something in my .config file (register type/ alias) and getting the below error:

The type name or alias ExcludeMemberNameMatchingRule could not be resolved. Please check your configuration file and verify this type name.

 

Thanks for an advice.

 

Cheers,

Yohan

May 25, 2012 at 6:19 AM

If you are not using aliases then you should set the type of the matching rule to be more fully qualified:

<matchingRule name="rule2" type="Namespace.ExcludeMemberNameMatchingRule, AssemblyName">
--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

May 25, 2012 at 6:34 AM

Hi,

I've managed to get it working through the following changes:

Config:

...

<!-- Ignore List -->
            <matchingRule name="ExcludeMemberNameMatchingRule" />

...

<register type="IMatchingRule" mapTo="ExcludeMemberNameMatchingRule" name="ExcludeMemberNameMatchingRule">
     <constructor>
        <param name="namesToExclude">
           <array type="string[]">
           <value value="methodtoexclude"/>
         </array>
       </param>
     </constructor>
   </register>
</container>

Thanks again for your help.

Cheers,

Yohan

May 25, 2012 at 6:46 AM

Hi Randy,

I've tried it your way and it works smoothly! 

 

Thanks a lot :)

Cheers,

Yohan

May 25, 2012 at 8:40 AM

Yes, you can configure either as Policy Injection or Unity Interception.  Both should work (once the configuration is sorted).  Glad to hear it's working.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jun 2, 2012 at 6:23 AM
Edited Jun 2, 2012 at 6:28 AM

Hi,

I wanted to achieve a better solution and thought about creating a custom matching rule constructor (e.g. CustomMatchingRule) that would filter both "namesToMatch" and "namesToExclude" in one rule.

The config would then look like:

<matchingRule name="rule1" type="CustomMatchingRule">
            <constructor>
              <param name="namesToMatch">
                <array type="string[]">
                  <value value="Get*" />
                  <value value="^GetCreditDetailInfo" />
                  <value value="^GetBankDetails" />
                </array>
              </param>
            </constructor>
</matchingRule>

 

The "^" sign denotes methods that should not be handled (to exclude).

 

Can you please advise how the code should be modified to fit this particular scenario?

 

Thanks,

Yohan

Jun 4, 2012 at 4:29 AM

I would actually change the IMatchingRule constructor to accept two arrays: one for matching, and one for exclusions.  Then the configuration would look like:

          <matchingRule name="rule1" type="MyCustomMemberNameMatchingRule">
            <constructor>
              <param name="namesToMatch">
                <array type="string[]">
                  <value value="Get*" />
                </array>
              </param>
              <param name="namesToExclude">
                <array type="string[]">
                  <value value="GetBankDetails" />
                </array>
              </param>
            </constructor>
          </matchingRule>

  Then in the Matches method create the logic to match and exclude:

    public class MyCustomMemberNameMatchingRule : IMatchingRule
    {
        private readonly List<Glob> matchingPatterns;
        private readonly List<Glob> excludePatterns;

        public MyCustomMemberNameMatchingRule(IEnumerable<string> namesToMatch, IEnumerable<string> namesToExclude)            
        {
            matchingPatterns = new List<Glob>();

            foreach (string name in namesToMatch)
            {
                matchingPatterns.Add(new Glob(name));
            }

            excludePatterns = new List<Glob>();

            foreach (string name in namesToExclude)
            {
                excludePatterns.Add(new Glob(name));
            }
        }

        public bool Matches(MethodBase member)
        {
            bool shouldProcess = matchingPatterns.Exists(pattern => pattern.IsMatch(member.Name))
                && !excludePatterns.Exists(pattern => pattern.IsMatch(member.Name));

            return shouldProcess;
        }
    }

If you wanted case insensitive matching or optional constructor parameters then you would have to add in the appropriate constructors.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jun 4, 2012 at 6:27 AM
Edited Jun 4, 2012 at 6:31 AM

Hi,

Thanks for this update Randy.  I've managed to use your suggestion and implement it the way I wanted to as follows:

Config:

<matchingRule name="MethodMatchingRule" type="myMatchingRule, Namespace">
     <constructor>
           <param name="namesToMatch">
              <array type="string[]">
                  <value value="Namespace.Get*"/>
                  <value value="^Namespace.GetUser"/>
              </array>
           </param>
      </constructor>
</matchingRule>

 

 

C# Code:

class CacheMatchingRule : IMatchingRule
    {
        private readonly List<Glob> matchingPatterns;
        private readonly List<Glob> excludePatterns;

        public CacheMatchingRule(IEnumerable<string> namesToMatch)     
        {
            matchingPatterns = new List<Glob>();
            excludePatterns = new List<Glob>();
            foreach (string name in namesToMatch)
            {
                if(!name.ToLower().StartsWith("^"))
                {
                    matchingPatterns.Add(new Glob(name.ToLower()));
                }
                else
                {
                    excludePatterns.Add(new Glob(name.ToLower()));
                }
              
            }

        }

        public bool Matches(MethodBase member)
        {
            var assemblyName = member.Module.Assembly.FullName.ToLower();
            var match = assemblyName.Substring(0, assemblyName.IndexOf(",", StringComparison.Ordinal)) + "." + member.Name.ToLower();

            bool shouldProcess = matchingPatterns.Exists(pattern => pattern.IsMatch(match))
                && !excludePatterns.Exists(pattern => pattern.IsMatch(match));

            return shouldProcess;
        }
    }

In that way it works for both Namespace matching and method name as well as an "ignore list".

Thanks again for your help - much appreciated :)

Cheers,

Yohan